4.2 KiB
layout | title | category | tags | order | ||||
---|---|---|---|---|---|---|---|---|
developer-doc | Projections and Field Access | syntax |
|
12 |
Projections and Field Access
Enso provides multiple ways for users to access data from their types. It has the old functional stalwart of pattern matching, but it also has an inbuilt notion of accessors based on lenses (field projections).
Pattern Matching
Pattern matching in Enso works similarly to as you would expect in various other functional languages. Typing information is always refined in the branches of a case expression, which interacts well with dependent typing and type-term unification. There are a few main ways you can pattern match:
-
Positional Matching: Matching on the scrutinee by structure. This works both for atoms and typesets (for typesets it is a subsumption judgement).
type Vector a V2 x:a y:a V3 x:a y:a z:a v = Vector.V3 x y z case v of Vector.V3 x y z -> print x
-
Type Matching: Matching purely by the types involved, and not matching on structure.
case v of Vector.V3 -> print v.x
-
Name Matching on Labels: Matching on the labels defined within a type for both atoms and typesets, with renaming.
case v of Vector.V3 {x y} -> print x {x} -> print x
-
Naming Scrutinees in Branches: Ascribing a name of a scrutinee is done using the standard typing judgement. This works due to the type-term unification present in Enso.
case _ of v : Vector.V3 -> print v,x
The actionables for this section :
- Refine the syntax for the name-based case.
- Provide code examples for why the renaming use-case is important (e.g. cases where there are clashing field names).
- Function-resolution matching.
The Underscore in Pattern Matching
An underscore _
passed as an argument to a syntactic pattern does not behave
like the function argument shorthand. Instead it acts as a positional match that
is given no name.
Projections
Unlike the simple accessors defined by most programming language, Enso's accessors are far more powerful. This is because they are based on lenses.
- Field accessors are standard lenses. As such, they can be used for both the getting and setting of properties.
- As a lens (e.g.
.field
) is a first-class function, they can be curried and passed around like any other function.
type Engine
type Combustion
power: Int
cylinder_count: Int
type Electric
power: Int
is_blue: Bool
type Vehicle
type Car
color: String
max_speed: Int
engine: Engine
type Bike
color: String
type Person
type Cons
name: String
vehicle: Vehicle
main =
p1 = Person.Cons "Joe" (Vehicle.Car 'pink' 300 (Engine.Combustion 500 8))
print $ p1.name # -> Joe
print $ p1.vehicle.color # -> pink
print $ p1.vehicle.max_speed # -> Some 300
print $ p1.vehicle.engine.power # -> Some 500
print $ p1.vehicle.engine.is_blue # -> None
p1.vehicle.color = 'red' # OK
p1.vehicle.max_speed = 310 # FAIL: security reasons. Allowing this
# in Haskell was the worst decision
# ever. After refactoring it
# silently does nothing there.
p2 = p1.vehicle.max_speed ?= 310 # OK
p3 = p1.vehicle.engine.power = 510 # FAIL
p4 = p1.vehicle.engine.power ?= 510 # OK
lens_name = .name
lens_color = .vehicle.color
lens_max_speed = .vehicle.max_speed
lens_power = .vehincle.engine.power
## Function like usage:
print $ lens_name p1
print $ lens_color p1
print $ lens_max_speed p1
print $ lens_power p1
p1 . at lens_name = ... # OK
The actionables for this section are:
- Fix the example above. It isn't correct.