Flow control via type equality in a generic function


I read article about generic programming in PureScript and a typical generic function requires to define a dedicated type-class (e.g. Show) and provide instances for all types which could appear as arguments of generic data constructors.

Is there a short cut to avoid such hassle when a generic function is interested in a particular type and a value of any other type is mapped with identity function?

Sort of Haskell gmap feature.

1 Like

Could you write up an example of how you would want such a thing to work in practice?

1 Like

But gmapT is a method of Data. So you do have to define a Data instance for your type in order to use gmapT on it.

Are you looking for automatic derivation of such class?

I gave a talk on this very topic a while back -
Video - Type reification with PureScript by Anupam Jain #FnConf 2022 - YouTube
Slides - https://speakerdeck.com/ajnsit/purescript-typeable

1 Like

Sure, I have a set of record types with type parameter f.

data ExprT f
  = Filter (FilterBodyT f)
  | Or (Array (f (ExprT f)))

type FilterBodyT f =
  { predicate :: f (PredicateT f)
  , trainedOn :: Array ChartId

The type variable f could be Maybe or Identity. Maybe is used in form state to represent partial value in a structured editor.
Server version of these types doesn’t have f - it i assumed that Identity is applied, so on PureScript side should be a function traversing such tree and replacing all Justs to pures.

finishDesign :: Maybe (ExprT Maybe) -> Maybe (ExprT Identity) 

Example with Show is natural, because every value is showed, but here non generic values are left intact and defining empty instances seems boring job.

Is there Data in PureScript? I haven’t found such thing.

My motivation is simple - I started writing PureScript after Haskell and my subconscious assumption that every problem X in PureScript is easier or as hard as in Haskell. PureScript is positioned as Haskell on frontend and talking about Haskell everybody think about type acrobatics first.

I found type equality library on purs, but I don’t see how to use it.

No, the is no Data in PureScript. But nothing is stopping you from making it. Declare the class, give it a gmapT method, make instances for all the types you need - and there you go, you can do everything you can do in Haskell.

The one difference would be that in Haskell you wouldn’t have to make instances for all the types manually, the compiler can do it for you. Which is why I’m asking: is that the bit you’re looking for? Or are you ok with defining instances yourself?

1 Like

Oh, and the equality library you found works at type level, you can’t use it for RTTI

May I recommend a different, equivalent (at least on this example) approach?

Instead of parametrizing on f :: Type -> Type, where the two instances of f are Maybe and Identity, consider parametrizing on i :: Type (for ‘incomplete’), where the two instances of i are Unit and Void.

Replace all applications of f in your types with Either i:

data ExprT i
  = Filter (FilterBodyT i)
  | Or (Array (Either i (ExprT i)))

type FilterBodyT i =
  { predicate :: Either i (PredicateT i)
  , trainedOn :: Array ChartId

Now derive Functor, Foldable, and Traversable for ExprT and PredicateT.

Assuming you can do the above, your desired finishDesign is:

finishDesign :: ExprT Unit -> Maybe (ExprT Void) 
finishDesign = traverse (const Nothing)

Yes, Haskell and PureScript allow a fair amount of type acrobatics, but you can often get farther by not trying to jump quite so high.

(If you are troubled by all the Rights that a serialization of an ExprT Void would contain, there’s no need to use literal Either for this; you could wrap it in a newtype with custom serialization rules.)

1 Like

There must be a reason, if nobody defined Data class before I discovered that fact.
In the ideal world I would get PureScript record types via translation of Haskell ones, but I haven’t get to such skill state yet.

Is there a Haskell solution for translating types to PureScript which can generate Data instances for PureScript as a bonus?

Purescript doesn’t have Data because it doesn’t have Typeable. Typeable in Haskell has its instances automatically derived by the compiler, but the PureScript compiler doesn’t have that feature yet. However you can work around that limitation with various caveats.

I had not linked to my code repo earlier, but if you take a look, I do have an implementation of Data and gmapT in my purescript-typeable library - purescript-typeable/src/Data/Data.purs at main · ajnsit/purescript-typeable · GitHub.

The caveat of my approach is that I depend on how the compiler does dictionary passing at runtime. But the benefit is that you don’t have to write Typeable instances yourself (apart from a very straightforward TagT instance which the compiler will prevent you from getting wrong). You can read the usage notes here - GitHub - ajnsit/purescript-typeable: Reified types for Purescript.


Thanks, I watched the video. instance chain approach should fit my case. Till then I thought that instance chain syntax is an unmerged feature.


Small update -

  1. I have changed the representation of Typeable to not depend on the internal dictionary passing mechanism. There is still a dependence on PureScript using stable dictionaries at runtime (which seems a much stronger guarantee), but the situation is now much better than earlier.

  2. I have added some notes on how to use Data.Data to the README - GitHub - ajnsit/purescript-typeable: Reified types for Purescript.

1 Like