purescript/purescript-record

Create a partially applied record constructor

chrisdone opened this issue · 7 comments

Consider this:

F <$> x <*> y <*> z

This has a problem traditionally that you need to make sure you get the x/y/z order correct. If you're making forms (web forms) with e.g. a formlet library, this becomes pretty hard to manage.

What if we could do this?

partially F <$> pure {x:a} <*> pure {y:b} <*> pure {z:a}

(or alternatively by using SProxy :: SProxy "a", etc.)

So that partially F would produce a function of one argument of one of the x, y or z fields (or more, I guess). When provided with one, it accepts another argument until all fields have been consumed.

I made special version of <*> that achieves something similar:

module Record.Apply where

import Control.Applicative
import Prim.Row (class Nub, class Union)
import Record (disjointUnion)

-- API

applyFields
  :: forall f inner outer combined.
     Union inner outer combined
  => Nub combined combined
  => Apply f
  => f { | inner }
  -> f { | outer }
  -> f { | combined }
applyFields getInner getOuter =
  disjointUnion <$> getInner <*> getOuter

infixl 5 applyFields as <|*>

-- Demo

newtype Foo = Foo { x :: Int, y :: String, z :: Array Int }

demo :: forall f. Applicative f => f Foo
demo = Foo <$> pure {y: ""} <|*> pure {x: 2} <|*> pure {z: []}

Which works nicely, but I think re-using <*> would be cool too. Any ideas?

garyb commented

I realise this is pretty old now, but for this kind of thing I've tended to do things like:

F <$> ({ x: _, y: _, z: _ } <$> a <*> b <*> c)

I guess the goal here was to make the applied field order non-specific though?

I guess the goal here was to make the applied field order non-specific though?

Right; so then one doesn't have to worry about argument order. 👍

I wonder if this wouldn't be better served by the general purpose "record sequence" feature (that exists somewhere, I'm sure, I just don't remember where off the top of my head).

F <$> sequenceRecord { x: pure a, y: pure b, z: pure a }

That's a very attractive way to express this problem!

Labeling as 'documentation' as it seems like the problem was addressed but the solution could be highlighted still.

It’s still an interesting area generally.

See also https://gist.github.com/chrisdone/8ea87b30d3897a031de3ceeec690815e

I have a language with row types and I want a way to combine applicative things. I’d love to get a solution that uses the native row types, without stating the same field names twice, and also permitting custom ordering (important for a form).

So far I’m not satisfied with any ideas I’ve had. I forgot about this thread.