Frege has built-in mechanism to access and mutate (non-destructive) record fields.
Consider the following type in Frege:
frege> data Point = Point {x :: Int, y :: Int}
data type Point :: *
frege> derive Show Point
instance Show Point
Now we can use the following functions to get and set record fields:
frege> Point.x
:: Point -> Int
frege> Point.{x = }
:: Point -> Int -> Point
frege> Point.{x <- }
:: Point -> (Int->Int) -> Point
For Field x
,
- The function
Point.x
is the getter. - The function
Point.{x = }
is a setter which sets the fieldx
with a new value. - The function
Point.{x <- }
is also a setter but applies a function to update the current value.
We can use the functions like this:
frege> p = Point 3 4
value p :: Point
frege> p
Point 3 4
frege> Point.x p
3
frege> Point.{x =} p 13
Point 13 4
frege> Point.{x <-} p (+15)
Point 18 4
Frege also provides some shortcuts to apply these functions:
frege> p.x -- Same as `Point.x p`
3
frege> p.{x = 10} -- Same as `Point.{x = } p 10`
Point 10 4
frege> p.{x <-} -- Same as `Point.{x <-} p`
:: (Int->Int) -> Point
frege> p.{x <- (+10)} -- Same as `Point.{x <- } p (+10)`
Point 13 4
Multiple updates can be combined:
frege> p.{x <- (+8), y = 20} -- Increment x by 8 and set y to 20
Point 11 20
Accessors and updates can be at any level deep. Let’s create another type:
frege> :{
> data Circle = Circle {center :: Point, radius :: Int}
>
> derive Show Circle
> :}
data type Circle :: *
instance Show Circle
frege>
Here we have an aggregate type Circle
which composes another type Point
for it’s field center
.
Now we can update and select fields at different levels:
frege> c = Circle {center = Point 4 5, radius = 10}
value c :: Circle
frege> c
Circle (Point 4 5) 10
frege> c.center.x
4
frege> c.{center <- Point.{x = 8}}
Circle (Point 8 5) 10
frege> c.{center <- Point.{y <- (+20)}, radius <- (*5)}
Circle (Point 4 25) 50
In the latest version, Frege provides syntactic sugar for lambdas using underscores. For example, T.foo
can be written
as _.foo
if the type can be deduced from the context the lambda is applied. Hence the following two are equivalent.
frege> c.{center <- Point.{x = 25}}
Circle (Point 25 5) 10
frege> c.{center <- _.{x = 25}}
Circle (Point 25 5) 10
Frege provides another utility to check for a field’s existence. This would be useful if we have multiple constructors with different set of fields.
frege> :{
> data Point = Point2d {x :: Int, y :: Int}
> | Point3d {x :: Int, y :: Int, z :: Int}
>
> derive Show Point
> :}
data type Point :: *
instance Show Point
frege>
In the above code, we have two constructors Point2d
and Point3d
where the field z
exists only for Point3d
.
We can check for the existence of field z
like this:
frege> Point.{z?}
:: Point -> Bool
frege> hasZ = Point.{z?}
frege> hasZ $ Point3d 3 4 5
true
frege> hasZ $ Point2d 3 4
false
frege> p = Point3d 3 4 5
value p :: Point
frege> p.{z?}
true
For more details on how these field existence check, accessor and mutator functions are generated for a record type, here is the link to Frege language reference: http://www.frege-lang.org/doc/Language.pdf.
Happy coding!
Comments