Data types, naming fields and field update syntax

I can’t get data with named fields with field update syntax working, and I’m not sure what I’m doing wrong or if it just doesn’t exist.

I can’t use “types” for everything because I’m getting type circles, and I need instances as well (and it appears types can’t be instances). But for many things, I would like field update syntax or its quite impractical.

What I want to do is

data T = T { field1 :: Int, field2 :: Number }

then something like the ability to choose either of the following ways to construct data:

t1 = T { field1: 0, field2: 0.0}
t1 = T 0 0.0

Then update with
t2 = t1 { field1 = 2 }

The latter two don’t work for me but I’m not sure how to interpret the errors I’m getting.

I think the issue you’re experiencing with t2 = t1 { field1 = 2 } is that t1 is not a record but of type T.

You should be able to do something like this:

module Foo where

import Data.Newtype (class Newtype, unwrap)

newtype T = T { field1 :: Int, field2 :: Number }

derive instance newtypeT :: Newtype T _

t :: Int -> Number -> T
t field1 field2 = T { field1, field2 }

t1 :: T
t1 = T { field1: 0, field2: 0.0}

t2 :: T
t2 = T (unwrap t1) { field1 = 2 }

t3 :: T
t3 = t 0 0.0
1 Like

Can you give me some more context? Is this the only way to be able to access or update fields by name? (other than type, which causes type circles in my application).

I’m coming from Haskell, so why is there a Newtype class? What is the hole doing in the “derive” statement?

So would this be correct:

  • updating by field name is not possible with data

  • updating by field name in a newtype requires “unwrap”?

  • there is no way to construct a newtype without naming the fields, unless you provide a function to do so (but that doesn’t count, after all you can provide a function to do anything)

I think these resources probably cover it but I’ll try to answer below as well.

It’s not the only way since PureScript has anonymous records, so t.field1 and _.field2 $ t are ways to access fields by name. If using a newtype or a record these would be (unwrap t).field1 and _.field2 <<< unwrap $ t.

Another way would be to use optics, which could handle setting/getting/updating fields and also account for the newtype wrapper.

_field1 :: Traversal' T Int
_field1 = _Newtype <<< prop (SProxy :: SProxy "field1")

_field2 :: Traversal' T Number
_field2 = _Newtype <<< prop (SProxy :: SProxy "field2")

preview _field1 t1

Correct. At least not in the way you might expect coming from Haskell, which is to say not without defining functions to destructure things first and then wrap them back up.

Aside from wrap/unwrap, you could use optics, or use functions like over to update the underlying record.

over T \t -> t { field1 = t.field1 + 1 }
over T _ { field2 = 0.0 }

If you mean a newtype of a record, then I think this is accurate.

1 Like

Quick note: if you use Lens' here, you can use view (and not worry about the Maybe result). Otherwise looks good to me!

1 Like