Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#102] Use morley capabilities to define custom ADT layout #112

Merged
merged 17 commits into from
Sep 29, 2020

Conversation

dcastro
Copy link
Contributor

@dcastro dcastro commented Sep 24, 2020

Description

Problem: The Michelson representation (that is, the balancing of pair
and ors) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced customGeneric to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.

Related issue(s)

Resolves #102

✅ Checklist for your Pull Request

Related changes (conditional)

  • Tests

    • If I added new functionality, I added tests covering it.
    • If I fixed a bug, I added a regression test to prevent the bug from
      silently reappearing again.
  • Documentation

    • I checked whether I should update the docs and did so if necessary:
    • I updated changelog unless I am sure my changes are
      not essential.

Stylistic guide (mandatory)

haskell/test/SMT.hs Outdated Show resolved Hide resolved
)

-- | Parse the stablecoin contract.
parseStablecoinContract :: MonadThrow m => m (T.Contract (ToT Parameter) (ToT Storage))
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we have that splice above, we know in compile time that the contract is valid (otherwise compilation fails). Can we use this knowledge here to avoid MonadThrow and change it to just T.Contract (ToT Parameter) (ToT Storage)? I am bad at TH, but I suspect it should be straightforward.

Copy link
Contributor Author

@dcastro dcastro Sep 25, 2020

Choose a reason for hiding this comment

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

I don't think we can, or at least not in that way.

The TH splice would have to compile the contract (already done), and then somehow emit a valid haskell expression of type T.Contract (ToT Parameter) (ToT Storage), as if it had been handwritten (not easily done).

In other words, what would the code generated by TH look like?

stablecoinContract :: T.Contract (ToT Parameter) (ToT Storage)
stablecoinContract = ???

Theoretically, the TH splice could pattern match on the parsed T.Contract (ToT Parameter) (ToT Storage), and build an Exp AST representing calls to Michelson.Typed.Instr and Michelson.Typed.Value. It is doable, but it's a lot of work for little gain.

However, because the TH splice proves the contract is well-typed, we can change either throwM to either error, and get rid of MonadThrow, and be sure that error will never be called. Yeah, it makes the eyes bleed, but it's "safe" :P

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay

However, because the TH splice proves the contract is well-typed, we can change either throwM to either error, and get rid of MonadThrow, and be sure that error will never be called.

I think it's a good idea (as long as there is a comment explaining why the error can't happen here). It matches the idea of using error for errors that can happen only in case of a programmer mistake.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, looks a lot better now!

Copy link
Collaborator

Choose a reason for hiding this comment

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

🆒

@dcastro dcastro force-pushed the diogo/#102-adt-layout branch from 700c145 to d58a80d Compare September 28, 2020 10:58
@dcastro dcastro force-pushed the diogo/#102-adt-layout branch from ac981dc to a287365 Compare September 29, 2020 10:38
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: The Michelson representation (that is, the balancing of `pair`
and `or`s) of our Haskell data types must match the Michelson
representation of our Ligo data types.

In the past, we used Haskell nested tuples in order to have complete
control of the layout, but this leads to messy, hard to maintain code.

Solution: Now that morley introduced `customGeneric` to control an ADT's
layout, we can replace our nested tuples with Haskell ADTs and still
have control of its layout.
Problem: We have two data types, `Storage` and `StorageView`, that are
exactly the same, except `StorageView` has BigMapIds instead of BigMaps.
As a consequence, we have to use `customGeneric` and declare the
storage's layout twice, and these must be kept in sync at all times.
This is a maintenance burden.

Solution: Merge these two data types into `Storage' big_map`, declare
the storage's layout once, and then define two type aliases for ease of
use.
Problem: The Michelson representation of Haskell's `Parameter` and
`Storage` must be an exact match of the Michelson repr of Ligo's
corresponding data types. They must always be kept in sync, which is a
maintenance burden.

Solution: to make this a little bit easier to maintain, we can
✨sprinkle✨ some TH and check at compile-time that the Michelson reprs
match.

Also moved `parseStablecoinContract` and `parseRegistryContract` from
`Stablecoin.Client.Contract` to `Lorentz.Contracts.Stablecoin`, because
these functions are not really related to the stablecoin-client, they
have other uses.
Problem: Now that we're using TH to check at compile-time that the
contract can be parsed and typechecked, we should no longer need the
MonadThrow constraint on parseStablecoinContract and
parseRegistryContract.

Solution: Inside a TH splice, after proving that the contract can be
parsed+typechecked, we emit a haskell expression that reads the contract
again at runtime, except it's now safe to use `error` instead of
`throwM` because we know this can't possibly fail at runtime.
@dcastro dcastro force-pushed the diogo/#102-adt-layout branch from a287365 to 63140ef Compare September 29, 2020 11:25
@dcastro dcastro merged commit 4963c8e into master Sep 29, 2020
@dcastro dcastro deleted the diogo/#102-adt-layout branch September 29, 2020 11:44
dcastro added a commit that referenced this pull request Oct 1, 2020
Problem: In #112, we started parsing the stablecoin and metadata
registry contracts at compile-time. We should do the same for the new
stablecoin FA1.2 contract too.

Solution: Parse the stablecoin FA1.2 contract at compile-time.
dcastro added a commit that referenced this pull request Oct 1, 2020
Problem: In #112, we started parsing the stablecoin and metadata
registry contracts at compile-time. We should do the same for the new
stablecoin FA1.2 contract too.

Solution: Parse the stablecoin FA1.2 contract at compile-time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use morley capabilities to define custom ADT layout
2 participants