2

Can someone tell me what is the use case of purescript-variants or variants in general

The documentation is very well written but I can't find any real use case scenario for it. Can someone tell how we could use Variants in real world?

1 Answer 1

2

Variants are duals of records. While records are sort of extensible ad-hoc product types (consider data T = T Int String vs. type T = { i :: Int, s :: String }), variants can be seen as extensible ad-hoc sum types - e.g. data T = A Int | B String vs. Variant (a :: Int, b :: String)

For example, just as you can write a function that handles a partial record:

fullName :: forall r. { first :: String, last :: String | r } -> String
fullName r = r.first <> " " <> r.last

myFullName = fullName { first: "Fyodor", last: "Soikin", weight: "Too much" }

so too, you can write a function that handles a partial variant:

weight :: forall r. Variant (kilos :: Int, vague :: String | r) -> String
weight = 
  default "Unknown"
  # on _kilos (\n -> show n <> " kg.")
  # on _vague (\s -> "Kind of a " <> s)

myWeight = weight (inj _kilos 100)  -- "100 kg."

alsoMyWeight = weight (inj _vague "buttload")  -- "Kind of a buttload"

But these are, of course, toy examples. For a less toy example, I would imagine something that handles alternatives, but needs to be extensible. Perhaps something like a file parser:

data FileType a = Json | Xml

basicParser :: forall a. FileType a -> String -> Maybe a
basicParser t contents = case t of
  Json -> parseJson contents
  Xml -> parseXml contents

Say I'm ok using this parser in most case, but in some cases I'd also like to be able to parse YAML. What do I do? I can't "extend" the FileType sum type post-factum, the best I can do is aggregate it in a larger type:

data BetterFileType a = BasicType (FileType a) | Yaml

betterParser :: forall a. BetterFileType a -> String -> Maybe a
betterParser t contents = case t of
  BasicType bt -> basicParser bt contents
  Yaml -> parseYaml contents

And now whenever I call the "better parser", I have to wrap the file type awkwardly:

result = betterParser (BasicType Json) "[1,2,3]"

Worse: now every consumer has to know the hierarchy of BetterFileType -> FileType, they can't just say "json", they have to know to wrap it in BasicType. Awkward.

But if I used extensible variants for the file type, I could have flattened them nicely:

type FileType r = (json :: String, xml :: String | r)

basicParser :: forall a r. Variant (FileType r) -> Maybe a
basicParser = onMatch { json: parseJson, xml: parseXml } $ default Nothing
----
type BetterFileType r = (yaml :: String | FileType r)

betterParser :: forall a r. Variant (BetterFileType r) -> Maybe a
betterParser = onMatch { yaml: parseYaml } basicParser

Now I can use the naked variant names with either basicParser or betterParser, without knowing to wrap them or not:

r1 = betterParser $ inj _json "[1,2,3]"
r2 = betterParser $ inj _yaml "foo: [1,2,3]"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.