<signed_datafeed_article>
<datafeed_article>
<datafeed_name>edgecase_datafeed</datafeed_name>
<datafeed_article_id>210</datafeed_article_id>
<date>2021-03-09
<note>This is the date at the time of creation of this datafeed article. A checkpoint article containing a hash of this datafeed article may be created on this date or at a later date.</note>
</date>
<previous_checkpoint>
<datafeed_article_id>144</datafeed_article_id>
<checkpoint_id>9</checkpoint_id>
<date>2020-05-28</date>
<transaction>
<blockchain_name>bitcoin</blockchain_name>
<transaction_id>b27618deae05910f529240cc6960aeb87f017b12d302327253ee893825ce2bd4</transaction_id>
<block_height>632100</block_height>
<source_address>1HtwyqFWNVDoSEVqZwjBRRAV2oEsi8aQXr</source_address>
<destination_address>13MfGs39pR5aEK4iKdoLjVYXKwi6Y3uyPq</destination_address>
</transaction>
</previous_checkpoint>
<article>
<title>Monads_in_Python</title>
<author_name>nicholas_piano</author_name>
<date>2021-02-21</date>
<signed_by_author>no</signed_by_author>
<content>

<heading_lines>
GOAL
</heading_lines>

To describe the monad pattern and its uses in Python.

<heading_lines>
CONTENTS
</heading_lines>

- Goal
- Contents
- Introduction
- Truth monad
- Error monad
- Case study: Bitcoin address generation
- Flexibility
- Conclusion

<heading_lines>
INTRODUCTION
</heading_lines>

A monad is a pattern in several programming languages that is sometimes referred to as "a programmable semicolon". This refers to many programming languages in which a semicolon is used to terminate an expression, delineating parts of a process within a scope.

A semicolon might appear simple, but it's possible to zoom out and think about its abstract semantic role. Consider the following code:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
const a = 10;
const b = a * 10;
</data_lines>
</lined_code>

JavaScript's complicated relationship with semicolons aside, the meaning of the semicolon on line 1 is essentially: "The variable <code>a</code> is no longer being assigned and is available to be referred to from this point forward."

More generally: "The execution of this statement is complete, so go to the next statement."

The idea of a monad is an expanded version of this. What if, instead of "execution is complete", the code encoded what it would do if an error occurred and was redirected by the "semicolon". An example of this can be found in JavaScript with promises:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
somePromise().then(() =\> {
  // code here is executed on resolve
}).catch(() =\> {
  // code here is executed on reject
})
</data_lines>
</lined_code>

The <code>.then()</code> wrapper is essentially a monad that guides the behaviour of the process. Another example can be found in Python using <code>with</code>:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
with open(filename, 'r') as file_handler:
  # regardless of an operation here, .close() will be called on file_handler before exiting the scope.
</data_lines>
</lined_code>

It's probably the case that <code>with</code> is not strictly a monad since it does not govern the behaviour of each individual expression. This article presents some examples of how the monad pattern can be implemented in Python and be used effectively when in a number of different applications.

The ultimate goal of this pattern is to present a process in a way that is easy to understand so that it can be modified and maintained quickly and easily.

<heading_lines>
TRUTH MONAD
</heading_lines>

The Truth monad can be used when running a series of related, but not interdependent, processes, each of which could end with a fatal error.

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
class Car:
  def start(self):
    self.startup_error = TruthMonad(
      self._check_key_in_ignition,
      self._check_key_turned,
      self._check_battery_charge,
      self._check_fuel_level,
    ).render()
</data_lines>
</lined_code>

The <code>TruthMonad</code> class governs the behaviour of each expression:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
class TruthMonad:
  def __init__(self, *funcs):
    self.error = None
    self._run(funcs)

  def _run(self, funcs):
    func = funcs[0]
    funcs = funcs[1:]
    error = func()

    if error:
      self.error = error

      return

    if funcs:
      self._run(funcs)

  def render(self):
    return self.error
</data_lines>
</lined_code>

Any of the functions could result in an error that would prevent the process from continuing. In this case, the function should return any truthy value, which will be assumed to be the error. If everything is proceeding as expected, the value <code>None</code> should be returned instead. This is a possible alternative to the following code:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
# Previous expression ...
error = expression_function()

if error:
  return error # Or some other way to stop the process

# Next expression ...
</data_lines>
</lined_code>

Or worse, a <code>try/catch</code>.

It's possible the <code>TruthMonad</code> class could be improved, but in this form, it can make a series of checks simpler and easier to update.

<heading_lines>
ERROR MONAD
</heading_lines>

The <code>ErrorMonad</code> class expands on <code>TruthMonad</code> by adding an explicit value for success and failure, akin to <code>.then()</code> in JavaScript.

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
class ErrorMonad:
  def __init__(self, data, *funcs):
    self.error = None
    self.result = None
    self._run(funcs, result=data)

  def _run(self, funcs, result=None):
    func = funcs[0]
    funcs = funcs[1:]
    result, error = func(result)

    if error:
      self.error = error

      return

    if funcs:
      self._run(funcs, result=result)

    else:
      self.result = result

      return

  def render(self):
    return self.result, self.error
</data_lines>
</lined_code>

This allows it to be used as a pipeline, passing the result of each expression to the next if it succeeds, and exiting the execution if there is an error value. This is useful for calculations that need to mutate a value in several stages to yield a final result. One very good example is the generation of a Bitcoin address.

<heading_lines>
CASE STUDY: BITCOIN ADDRESS GENERATION
</heading_lines>

To generate a Bitcoin address, the process must start with an ECDSA private key, which is simply a string of 64 hex characters, representing 32 bytes of data. This data must be taken through a series of checks and transformations to yield the Bitcoin address.

Using the <code>ErrorMonad</code> class, the process can be represented as follows:

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
class Address:
  def __init__(self, secret_key=None):
    self.secret_key = secret_key

    if self.secret_key:
      self.is_valid = False
      self._generate()

  def _generate(self):
    self.value, self.error = ErrorMonad(
      self.secret_key,
      self._validate_secret_key_hex,
      self._validate_secret_key_length,
      self._validate_secret_key_integer_domain,
      self._convert_secret_key_to_public_key,
      self._convert_public_key_to_sha256_digest,
      self._convert_sha256_digest_to_ripemd160_digest,
      self._prepend_bitcoin_chain_flag,
      self._append_checksum,
      self._convert_hex_to_base58,
    ).render()

    if not self.error:
      self.is_valid = True
</data_lines>
</lined_code>

Note that the initial data, <code>self.secret_key</code>, is passed as a first argument. This is the <code>data</code> argument to the <code>ErrorMonad</code> constructor.

This requires that any function included in this process return two values; one to represent the error, and one to represent the value resulting from that step.

<heading_lines>
FLEXIBILILTY
</heading_lines>

The Bitcoin address generation process is done entirely using the <code>ErrorMonad</code> class, but it could be mixed with <code>TruthMonad</code> for the parts of the process that are not required to pass a result.

<lined_code>
<line_numbers>yes</line_numbers>
<data_lines>
class Address:
  def __init__(self, secret_key=None):
    self.secret_key = secret_key

    if self.secret_key:
      self.is_valid = False
      self._generate()

  def _generate(self):
    self.value, self.error = ErrorMonad(
      self._validate,
      self._transform,
    ).render()

    if not self.error:
      self.is_valid = True

  def _validate(self):
    self.error = TruthMonad(
      self._validate_secret_key_hex,
      self._validate_secret_key_length,
      self._validate_secret_key_integer_domain,
    ).render()

    return None, self.error

  def _transform(self):
    return ErrorMonad(
      self.secret_key,
      self._convert_secret_key_to_public_key,
      self._convert_public_key_to_sha256_digest,
      self._convert_sha256_digest_to_ripemd160_digest,
      self._prepend_bitcoin_chain_flag,
      self._append_checksum,
      self._convert_hex_to_base58,
    ).render()
</data_lines>
</lined_code>

The priority is to make sure the code is as clear as possible, so this might not be ideal in this case, but in general, these monad classes can be composed as needed.

<heading_lines>
CONCLUSION
</heading_lines>

Python may not have the in-built idea of monads in the strictest sense, but the pattern can inspire related machinery to make code clearer. Other requirements, such as dealing with multiple initial values, can be constructed in a similar manner.

</content>
</article>
</datafeed_article>
<datafeed_signature>
iQIcBAABCgAGBQJgR4DPAAoJECL1OzZgiBhwjsoP/0iraMcfwoxl9lMjRMQtBxDR
2d0L2RKBL4cDltHm1pknU146SNhYzxo5zKbEA/XCPL87l71w7zrIm3b0G+FgY5tm
NzJbVNqsXpjgMw8LFkIG3SL4LVW6p2ZGADY7970lsT0A4gz2BXw7PXJdHQk4OJXH
9/vmx2R4r9fBcAYzFOEuxItBFvLfRomDnRQFrHs0imVa9g1ndB+rmZktx3SCYrN6
R5BvqIqlCKIg9Ds1EzQZ8kxX+dPhfqYebS7L5+kYLgVodFwk+L9imucWtgPKomgm
N3MqrfaD6bpsgLI60hNbsv+e0tLEiEYcyISLGhxDKty9wiBRLAVDSTKrdu4CZS4d
JIQJYlr6YyK99zJvPzmoZAUMSt8KEGOK3M1rSovxIFJB5Mnk1joGjQXVp4tP5xtL
Rzw5YiZ+BblzK51i6IQ4egGJrpWSlx0U72nHlTMXCrZa/c385Qgaz6sokJ+wueB9
LIU7azbimPY7ZYeo+oK4G4LqTqyOAaPszU2WvCvWg1JZmOeqU6EC6CEkg6Nt/67k
TcVyBitwHNt5G6Lk/V1Sz+Jz44IxMly+UnCzhcM8hqixG2TbYuv9GQWIv5hsu3L/
7DfeVLqGMVrq5Li35OLxHwZESJscCmkjaMfD8tOYOHX8jOnFe5NhyuwN5A83+hR6
N8i6+1xubNHbjZdVpr5i
=5f1i
</datafeed_signature>
</signed_datafeed_article>