haskellpurescript

Applicative functors and records in purescript


In Haskell, I'm used to doing stuff like this.

data Foo = Foo { foo :: String, bar :: String }

mFoo :: (Monad m) => m String -> m String -> m Foo
mFoo foo bar = Foo <$> foo <*> bar

The same does not work in purescript, however. Is there a way to achieve the same outcome, ie keep the record syntax while allowing partial application via applicative functors when building the instance?

Thanks!


Solution

  • In PureScript you don't have to define such a record type upfront. Polymorphic version:

    mXY :: forall a b m. Apply m => m a -> m b -> m { x :: a, y :: b }
    mXY foo bar = { x: _, y: _ } <$> foo <*> bar
    
    -- | which can be rewritten using lift2:
    mXY' = Control.Apply.lift2 { x: _, y: _ }
    

    And a monomoprhic equivalent:

    type Foo = { x :: String, y :: String }
    
    mFoo :: forall m. Apply m => m String -> m String -> m Foo
    mFoo = Control.Apply.lift2 {x: _, y: _ }
    

    A "live demo" using wonderful try.purescript.org + custom gist:

    https://try.purescript.org/?gist=a37f5f0c50e0640e34ea5a4788c0c999

    Of course there is a practical value in using a newtype around Record too like:

    newtype Foo' = Foo' { x :: String, y :: String }
    

    and in such a case I would propose something like:

    mFoo'' foo bar = map Foo' $ { x: _ , y: _ } <$> foo <*> bar
    

    but I think that this could be expressed in a more elegant and shorter way which I don't know :-)

    EDIT:

    This is probably even nicer syntax using ado:

    ado
      x <- foo
      y <- bar
      in Foo { x, y }