purescript / purescript-record

Functions for working with records and polymorphic labels
BSD 3-Clause "New" or "Revised" License
70 stars 31 forks source link

Create a partially applied record constructor #49

Open chrisdone opened 6 years ago

chrisdone commented 6 years ago

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 5 years ago

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?

chrisdone commented 5 years ago

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. :+1:

MonoidMusician commented 5 years ago

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 }

chrisdone commented 5 years ago

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

justinwoo commented 5 years ago

I think Adam Saleh is the author of this part: https://github.com/justinwoo/purescript-record-extra/blob/685ac456ee8157403b34833d3989754a2ae237ea/src/Record/Extra.purs#L182

JordanMartinez commented 2 years ago

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

chrisdone commented 2 years ago

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.