Skip to content
This repository has been archived by the owner on Aug 14, 2019. It is now read-only.

Monadic pH tests #1152

Merged
merged 24 commits into from
Apr 25, 2019
Merged

Monadic pH tests #1152

merged 24 commits into from
Apr 25, 2019

Conversation

philipcmonk
Copy link
Contributor

I rejiggered pH tests to have monadic operations and satisfy the monad laws. This allows them to be fully composable, which makes it much easier to build more complex tests in terms of simpler ones.

This also includes "philters", which are stateful wrappers around tests. In lib/ph/azimuth.hoon we implement a mock ethereum node as a philter.

I refactored lib/ph.hoon into five files:

  • lib/ph.hoon: the ph monad.
  • lib/ph/tests.hoon: small library of tests that may be composed into larger tests.
  • lib/ph/util.hoon: useful functions for building raw tests.
  • lib/ph/philter.hoon: the philter framework.
  • lib/ph/azimuth.hoon: a philter for mocking an ethereum node.

A few notes on naming:

The name of the type of the monad was "data", but I've changed it to "form". If we write more monads, we should try to be consistent with these names. "data" is meaningless and yet still misleading (sure, code is data, but we don't need to rub it in their faces), "M" (following the notation used in papers) is problematic, and "t" (following ML) is one letter and claimed for "tail of a list" and "fifth element of a quintuple". "form" is unused in the standard library, though there are a couple of uses of it in more specific contexts. However, that sort of collision isn't relevant here because you should always be qualifying your monadic operations; e.g. form:(ph ,~).

I've changed "return" to "pure". "return" is a weird word that is likely to confuse anyone who's more familiar with imperative programming than monads. "unit" is taken, so "pure" is the other standard name for it. I like that it gives the idea of "don't do anything special or stateful or effectful, just produce this result".

Then, a monad in this form looks like:

++  babys-first-monad
  |*  a=mold
  |%
  ++  form  (unit a)
  ++  pure
    |=  arg=a
    ^-  form
    (some arg)
  ::
  ++  bind
    |*  b=mold
    |=  [m-b=(unit b) fun=$-(b form)]
    ^-  form
    (biff m-b fun)
  --

I would write a lead superclass for monads, but wet gates don't nest in anything.

@philipcmonk philipcmonk requested a review from joemfb April 22, 2019 23:23
Copy link
Member

@joemfb joemfb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Procedurally, I'm not sure what branch this should target, or when it should be merged (cc @jtobin). I assume #1150 should be merged first.

@philipcmonk
Copy link
Contributor Author

It does depend on #1150

@jtobin
Copy link
Contributor

jtobin commented Apr 23, 2019

I'll take a look at this in detail as I need to get in the habit of doing this sort of thing anyway. I'm guessing we can merge it into next after the PR it depends on goes in; I'll also make a point to clarify / implement my new branching ideas post-haste.

@ohAitch
Copy link
Contributor

ohAitch commented Apr 23, 2019

Should lib/generators be refactored to also be using an explicitly monadic style? (That's the like, preexisting IO monad that is similarly used for forms, and also does the impossible to migrate in-flight "wrap it all in a continuation" thing; still hoping to figure out a way to add wires / explicit state to both tbh)

@philipcmonk
Copy link
Contributor Author

I do think that would look cleaner at the call site with ;<.

I do hope to eventaually extend something that looks like the ph monad to be able to upgrade in-flight. It's not necessary for ph tests (just nuke 'em on upgrade), but obviously for most apps/vanes you want that. Basic strategies are (1) include a prep/stay arm in the monad type; (2) occasionally "save your state" explicitly; on reload just start from that point; (3) record "inputs" and replay them (similar to how old ford handled this).

I thought you would get a kick out of this:

++  almost-sum
  :<  sum=@  _cork  add
  (dec sum)

If you call (almost-sum 5 7) you get 11.

Useful if you want to define a function which first sends all its arguments to another function (in my case, +generate-public-key first calls +generate-private-key then uses that result to get the public key). I decided against using this because it's sure to confuse people.

@jtobin jtobin changed the base branch from master to next April 25, 2019 07:10
@jtobin jtobin merged commit d2724bb into next Apr 25, 2019
@jtobin jtobin deleted the philip/ph-monad branch April 25, 2019 07:13
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants