/Functional-Programming-in-Python

An attempt to implement some key concepts of the Functional Programming Paradigm in Python

Primary LanguagePython

Functional Programming in Python

An attempt to improve the Functional programming paradigm supported by Python.

Monads

Since the start of my Computer Science course, I became quite affictionate to the concept of functional programming, one of the key concepts of the paradigm are monads which I tried to implement in Python.

  • Functional languages use monads to turn complicated sequences of functions into succinct pipelines that abstract away control flow, and side-effects.

  • Monads provide a way to structure a program.

  • They can be used (along with abstract data types) to allow actions (e.g. mutable variables) to be implemented safely.

The Monads.py file

3 monad classes:

  • Maybe Monads: It can bind and flag if it contais a value.

  • Failure Monads: It can bind, consecutively bind and abstract exceptions.

  • Lazy Monads: It can bind, consecutively bind and late compute.

A parallelazible pool of Failure Monads:

If you want to use many Failure Monads that do not deppend on each other, why not parallelize it? Using this class you can choose:

Documentation

Import all classes from the Monads.py file

  from Monads import * 

Create a Maybe Monad with a value inside:

  MaybeMonad(value)
Parameter Type Description
value number Not optional. The value of your monad.

Bind a Maybe monad with a function:

  MaybeMonad(value).bind(function)
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Get the value from a Maybe monad:

  MaybeMonad(value).bind(function).value
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Example:

  def square(x):
    return x**2
  print(MaybeMonad(100).bind(square).value)

Returns:

  10000 #It applied the function to the value of the monad.

Example:

  def square(x):
    return x**2
  print(MaybeMonad(100).bind(square).bind(square).value)

Returns:

  1000000 #It applied the function to the value of the monad twice

Create a Failure Monad with a value inside:

  FailureMonad(value)
Parameter Type Description
value number Not optional. The value of your monad.

Bind a Failure Monad with a function:

  FailureMonad(value).bind(function)
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Get the value from a Failure Monad:

  FailureMonad(value).bind(function).value
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Example:

  def square(x):
    return x**2
  print(FailureMonad(100).bind(square).value)

Returns:

  10000 #It applied the function to the value of the monad.

Example:

  def square(x):
    return x**2
  print(FailureMonad(100).bind(square).bind(square).value)

Returns:

  1000000 #It applied the function to the value of the monad twice

Multiple argument functions support:

  def div(x, y):
    return x/y
  print(FailureMonad(100).bind(div, 10).value)

Returns:

  10 #It applied the function to the value of the monad and the argument.

Example:

  def div(x, y):
    return x/y
  print(FailureMonad(100).bind(div, 10).bind(div, 10).value)

Returns:

  1 #It applied the function to the value of the monad and the argument twice.

The key difference of Failure Monads is error handling:

  def div(x, y):
    return x/y
  print(FailureMonad(100).bind(div, 0).value)

Returns:

  None #Instead of rising an Exception it returs None and updates the error status.

Get the error status of a failure monad

  
  FailureMonad(value).bind(function).error_status
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Example:

  def div(x, y):
    return x/y
  print(FailureMonad(100).bind(div, 0).error_status)
  #It returns a dictionary containing every info of the exception/error.

Create a Lazy Monad with a value inside:

  LazyMonad(value)
Parameter Type Description
value number Not optional. The value of your monad.

Bind a Lazy Monad with a function:

  LazyMonad(value).bind(function)
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

The key difference of Lazy monads is late computing, it only computes when asked.

  FailureMonad(value).bind(function).compute()
Parameter Type Description
value number Not optional. The value of your monad.
function function Not optional. A funtion to bind the value of the monad with.

Example:

  def square(x):
    return x**2
  print(LazyMonad(100).bind(square).compute())

Returns:

  10000 #It applied the function to the value of the monad.

Example:

  def square(x):
    return x**2
  print(LazyMonad(100).bind(square).bind(square).compute())

Returns:

  1000000 #It applied the function to the value of the monad twice

Multiple argument functions support:

  def div(x, y):
    return x/y
  print(LazyMonad(100).bind(div, 10).compute())

Returns:

  10 #It applied the function to the value of the monad and the argument.

Example:

  def div(x, y):
    return x/y
  print(LazyMonad(100).bind(div, 10).bind(div, 10).compute())

Returns:

  1 #It applied the function to the value of the monad and the argument twice.

Since Lazy Monads do not handle errors like Failure Monads:

  def div(x, y):
    return x/y
  print(LazyMonad(100).bind(div, 0))
  # This does not rise an exception because it hasn't computed it yet

So if we compute it, then it rises an Exception because we are dividing by zero:

  def div(x, y):
    return x/y
  print(LazyMonad(100).bind(div, 0).compute())
  # Rises an Exception because we can't divide by zero.

Consecutive Binds Method: