Skip to content

Conversation

tochicool
Copy link
Contributor

@tochicool tochicool commented Oct 29, 2022

Attempts to resolve #39

This introduces the bech32EncodeResult and bech32DecodeResult functions and their respective structured types to allow for custom application handling of failures. This also exports the maxBech32Length definition.

@tochicool tochicool force-pushed the bech32-structured-results branch from a4cc266 to 990f9c5 Compare October 30, 2022 11:32
This introduces the `bech32EncodeResult` and `bech32DecodeResult` functions and their respective structured types to allow for
custom application handling of failures. This also exports the
`maxBech32Length` function.
@tochicool tochicool force-pushed the bech32-structured-results branch from 990f9c5 to 0425635 Compare October 30, 2022 11:34
Copy link
Contributor

@GambolingPangolin GambolingPangolin left a comment

Choose a reason for hiding this comment

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

I really like the new functions. I'm going to be looking out for other places where we can succeed with errors instead of failing outright :)

@@ -1,5 +1,8 @@
{-# LANGUAGE DuplicateRecordFields #-}
Copy link
Contributor

Choose a reason for hiding this comment

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

I am on the fence about explicitly ambiguous record fields. The case for allowing it here is pretty strong:

  • The *Result types are unlikely to be used as type-class instances
  • They are likely to be consumed quite near where they are produced

Some inconveniences:

  • In order to use, e.g. result for both types in the same module DisambiguateRecordFields is needed (correct me here if I'm wrong)
  • Downstream code might require more explicit type annotations as a result

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, DisambiguateRecordFields can be used if you want to use the record field but have both record fields imported in your module with the same, or no, qualifier. Since GHC 9.20, you can also now also just turn on OverloadedRecordDot and use x.result anywhere without any real downsides. I tend to turn on DuplicateRecordFields very liberally, but it would be nice if record fields had their own namespace and weren't automatically functions with the same without some explicit intent.

How do ambiguous record fields interact with type class instances that make them more inconvenient? Just curious.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah it would be great if you could disambiguate by using Ctor.someField or something.

Hm. I guess it's just an intuitive concern that ambiguous record fields can hurt type inference. Sometimes code is accidentally more general than it seems. Suppose you have a type class where Bech32EncodeResult is a instance but Bech32DecodeResult is not. If you wrote some code (unannotated like in a where clause) which was accidentally polymorphic over these two types, it would not be possible to compose it with the type class members without providing an additional annotation. To contrast, if the fields are unambiguous the code might end up being monotype instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. I've pushed the changes with the field names disambiguated. Added it for all fields to keep it consistent as they may share more fields later on.

Copy link
Contributor

@GambolingPangolin GambolingPangolin left a comment

Choose a reason for hiding this comment

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

Well done

@tochicool
Copy link
Contributor Author

Please squash the commits!


-- | The result of encoding a 'Bech32' string
data Bech32EncodeResult = Bech32EncodeResult
{ encodeResult :: Text
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we wanna make this !Text?

Copy link
Contributor

@GambolingPangolin GambolingPangolin Nov 1, 2022

Choose a reason for hiding this comment

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

I think there is a good case to be made for making this field lazy. After all consumers may check one of the error fields and decide to throw away the encoding. Maybe we can reduce indirection for bech32Encode by changing the last line to

encodeResult `seq` pure encodeResult

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is the reason for wanting seq there to reduce some unwanted laziness? I think the following would no longer terminate if encodeResult was somehow not total, where it would have previously otherwise, ie:

case bech32Encode input of { Just _ -> "Valid bech32"'; Nothing -> "Invalid bech32" }

Happy to go with that if we're all convinced that encodeResult is always total. I'm also fine with keeping existing definitions for bech32Encode / bech32Decode if we're worried about the extra overhead caused by the lazy data types.

Copy link
Contributor

Choose a reason for hiding this comment

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

That was a misguided suggestion. Let's leave things as is. FWIW that making encodeResult a strict field has the same effect with respect to making bech32Encode diverge.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My suggestion was rooted in the perhaps false assumption that we could predict that the overwhelming majority of the time we would want to read the decoded result out. If that isn't the case then we can leave it as is.

@ProofOfKeags ProofOfKeags merged commit ad02fdd into haskell-bitcoin:master Nov 4, 2022
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.

Expose structured result types for bech32 encoding and decoding
3 participants