Showable type - PureScript

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Mason Mackaman

Can exists be used to implement a Showable type like what is done here? I don't see how to get the Show constraint.

rhendric

Nope. purescript-exists uses a direct unsafeCoerce-based representation of the existential, instead of the Church encoding which uses a lambda. The Church encoding could let you pass extra data in the closure of the lambda, but the coercion-based encoding can only represent unconstrained existentials.

Mason Mackaman

so does exists have any advantage over the church encoding method?

rhendric

Performance, probably?

rhendric

https://github.com/purescript/purescript-exists/issues/5

I guess it save the allocation of one lambda? Just wondered if there were any other reasons to prefer the current version. For reference, I'm talking about this as the alternative: newtype Fxis...
Mason Mackaman

huh, one less lambda allocation doesn't seem worth it to me to give up that much power, but :shrug:

rhendric

Well, switching encodings isn't necessarily enough to get you what you want either.

rhendric

I don't think the type system lets you express an Exists that is generic over constrained and unconstrained types.

Mason Mackaman

Are you saying you need to implement an ad hoc one whenever you need it?

rhendric

I think so, until PureScript gets constraint kinds or something.

Mason Mackaman

hm, I kinda assumed that bit in the article that was like

(Question: can we write this using the type Exists from above?)

was included as sort of an exercise to the reader, which made me guess that it was possible somehow, but perhaps it was a legitimate question

Mason Mackaman

and the answer is "no"

rhendric

Or it's both: an exercise for the reader to which the answer is ‘no’. :big_smile:

rhendric

What you need to generalize Showable is something like:

newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. c a => a -> r) -> r)
rhendric

But that c a => isn't legal.

Mason Mackaman

rhendric said:

Or it's both: an exercise for the reader to which the answer is ‘no’. :big_smile:

good point! :big_smile:

Mike Solomon

rhendric said:

What you need to generalize Showable is something like:

newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. c a => a -> r) -> r)

A bit late to the party. It depends what you want to achieve. If it is passing a dictionary around a la SYTC:

-- hydrate dictionary with whatever...
newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. { show :: a -> String, eq :: a -> a -> Boolean } -> a -> r) -> r)

This strategy can already go quite far - it's possible to handle fixed points, induction, etc.

rhendric

True, but I'm not sure that's quite in the spirit of the referenced blog post. Explicitly repacking your typeclass dictionary in that way is a decent workaround for not having constraint kinds, but it doesn't offer the ergonomics of passing something that is Show in and getting something that is Show out, at least not without some Show-specific boilerplate comparable to just writing Showable.

Mason Mackaman
-- hydrate dictionary with whatever...
newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. { show :: a -> String, eq :: a -> a -> Boolean } -> a -> r) -> r)

I'm confused about what is going on here. c doesn't appear on the right side at all. At first I thought

newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. c -> a -> r) -> r)

is what was meant, that way you could set up your constraints by setting c, but given that c relies on a, which is inside a higher rank type, I would think you still couldn't give it arbitrary constraints in this way. Is there something I'm missing?

Matei Adriel

What about

newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. c a -> r) -> r)
Matei Adriel

You could then pass it something like

type ShowConstraint a = Show a => a
Matei Adriel

(Idk if that works, cant try it rn)

TheMatten

It helps to mentally replace => with -> (which is what it is at runtime) - Show a -> a is a function from Show instance to a value, but you probably want equivalent of Show a /\ a

rhendric

Also, you can't partially apply type synonyms, so ConstrainedExists ShowConstraint wouldn't be valid anyway.

rhendric

This, however, works:

newtype ConstrainedExists c = ConstrainedExists (∀ r. (∀ a. c a r) -> r)

newtype ShowConstraint a r = ShowConstraint (Show a => a -> r)

newtype Hidden = Hidden Int

x :: ConstrainedExists ShowConstraint
x = ConstrainedExists (\(ShowConstraint f) -> f 72)

-- error: No type class instance was found for Data.Show.Show Hidden
--y :: ConstrainedExists ShowConstraint
--y = ConstrainedExists (\(ShowConstraint f) -> f (Hidden 72))
rhendric

But it doesn't save you from having to make that Show-specific newtype.

Mason Mackaman

why does the above approach work but not @Matei Adriel's :thinking:

Matei Adriel

@rhendric I used a type synnonim for the Exists type as well

Matei Adriel

Im not sure, imma experiment w it a lite bit more