2

I have a query type data Query a

Given that Query has no constructors, how can I define an eval function of type:

eval :: Query ~> H.ComponentDSL State Query Void m

Would I have to add a constructor to Query?

glennsl
  • 28,186
  • 12
  • 57
  • 75
whbb
  • 520
  • 5
  • 13

1 Answers1

0

Use type Query = Const Void and eval = absurd << un Const

type Query = Const Void = data Query a, They all have type Kind -> Kind, and have no constructor.

For eval = absurd <<< un Const match type Query ~> H.ComponentDSL State Query Void m. absurd :: forall a. Void -> a make sure return type match. un Const :: forall a b -> Const a b -> a make sure input type match (forall a. Query a -> Void)

whbb
  • 520
  • 5
  • 13
  • Could you explain this a bit? – glennsl Oct 23 '18 at 07:39
  • @glennsl You can check this issue https://github.com/slamdata/purescript-halogen/issues/582 – whbb Oct 24 '18 at 08:29
  • 1
    Thanks, but there isn't really much explanation there either. And in any case my point was more that SO as a knowledge base is much better if the answers are explained well (in the answer itself, not via a link to an explanation somewhere else). Code-only answers without explanation (and link-only answers) are often just deleted around here. I understand if you don't know the explanation yourself, though, and thanks for posting the answer anyway. Perhaps someone else could offer an explanation. – glennsl Oct 24 '18 at 08:59
  • Sorry, I am not a native English speaker, My explanation will be difficult to understand. `type Query = Const Void` = `data Query a`, They all have type `Kind -> Kind`, and have no constructor. For `eval = absurd <<< un Const` match type `Query ~> H.ComponentDSL State Query Void m`. `absurd :: forall a. Void -> a` make sure return type match. `un Const :: forall a b -> Const a b -> a` make sure input type match (`forall a. Query a -> Void`). – whbb Oct 25 '18 at 03:44
  • @glennsl The `Void` type is uninhabited, which means we can provide a function from `Void` to anything: `absurd`. `Void` has kind `Type`, but a query needs to be of kind `Type -> Type`, so we put it in `Const`, the constant functor, to get the right kind. – gb. Oct 25 '18 at 10:57
  • @gb Thanks! So I gather by "uninhabitated" you mean a type that has no values, and therefore anything requiring a `Void` cannot be constructed or evaluated? Could you explain `absurd` and `un` as well? I reckon `absurd` is a function that takes a `Void` and therefore can never be evaluated, and `un` would somehow unwrap the `Void` value from the `Const` constructor if it could be evaluated? – glennsl Oct 25 '18 at 14:26
  • 2
    @glennsl yeah, that's right - it has no values so cannot actually exist at runtime. `absurd` is implemented as an infinite loop - which is safe, as we know it can never be called (since there's no `Void` value). `un` is a function that unwraps `Newtype`s, the `Const` constructor it accepts is to aid type inference (and is also kinda code-as-documentation). Just as a `Void` value cannot exist at runtime, nor can a `Const Void`, as we'd need a `Void` value to construct one of those too, so `absurd <<< un Const` is essentially just proof for the type checker - it will never be used at runtime. – gb. Oct 25 '18 at 18:16
  • @gb Awesome, this makes complete sense to me now. Thanks for the explanation! I would love for this to be formalized into an answer in one way or another, since there's quite a few interesting and uncommon concepts in those two small definitions, and I think other newcomers coming across this would find it just as enlightening as I did. – glennsl Oct 27 '18 at 19:44