Anyone is able to create one of those.
Example Val Tagged MetersTag ... Float
→ The right choice, as every Float is a valid description of Meters
Create a new tagged value.
Checked with isCheckedInternal/Public when annotated that wayModifying won't change the type.
Alter the value inside.
If the Val was a Checked, it becomes a Tagged.
type alias Meters =
Val Tagged MetersTag Public Int
go1km : Meters -> Meters
go1km =
Val.map ((+) 1000)Use the values of 2 Vals to return a result. The result becomes a Tagged.
type alias PrimeNumber =
Val Checked PrimeNumberTag Public Int
prime3 : PrimeNumber
prime3 =
tag 3 |> isChecked PrimeNumber
prime5 =
tag 5 |> isChecked PrimeNumberIn another module
type alias NonPrime =
Val Checked NonPrimeTag Public Int
fromMultiplyingPrimes : Prime -> Prime -> NonPrime
fromMultiplyingPrimes aPrime bPrime =
Val.map2 (*) aPrime bPrime
|> isChecked NonPrimeOnly someone with access to the tag constructor is able to create one of those.
In effect, this means that you can only let "validated" data be of this type.
Examaple ... Checked ... NaturalNumberTag Int
→ ✓ not every Int can be called a NaturalNumber, it must be checked!
After calling tag or modifying a checked value, you get a Tagged. To tell the type that the result value is Checked, use isChecked tag.
The type of tag might even change in that operation.
oddPlusOdd : Odd -> Odd -> Even
oddPlusOdd oddToAdd =
Val.map2 (+) oddToAdd
>> isChecked EvenAnyone is able to access the value.
Use the values of 2 Accessibles to return a result.
type alias Prime =
Val Checked PrimeTag Public Int
prime3 =
tag 3 |> isChecked Prime
prime5 =
tag 5 |> isChecked PrimeAnywhere
val2 (+) prime3 prime5
--> 8
val2 Tuple.pair prime3 prime5
--> ( 3, 5 )
if val2 (<) onePrime otherPrime thenOnly the ones with access to the tag constructor can access the Val.internal.
Meaning that access can be limited to
module Special exposing (Special)
type alias Special =
Val Tagged SpecialTag Internal SpecialValueChecked)src
└ Special
└ Internal.elm
module Special.Internal exposing (SpecialTag(..))
└ SpecialPartA.elm
module SpecialPartA exposing (SpecialPartA)
import Special.Internal exposing (SpecialTag(..))
└ SpecialPartB.elm
module SpecialPartB exposing (SpecialPartB)
import Special.Internal exposing (SpecialTag(..))
elm.json 'exposed-modules' :
[ "SpecialPartA", "SpecialPartB" ]This generally helps hiding implementation details.
type alias OptimizedList a =
Val Checked OptimizedListTag Internal (Implementation a)
type alias Implementation a =
{ list : List a, length : Int }
toList --...If you have an Internal, its value isn't readable by users.
If you have the tag however, you can access this data hidden from users.
A Codec to serialize Tagged Public Vals.
A Codec to serialize Checkeds.
We don't trust that the values we encode still have the same promises as our Checkeds.
Choose a value it can convert from & to and serialize that.
module Nat exposing (serialize)
type NatTag
= Nat
serialize =
Serialize.int
|> Val.serializeChecked Nat
(\int ->
if int >= 0 then
Ok int
else
Err "Int was negative, so it couldn't be decoded as a Nat"
)
module EfficientList exposing (serialize)
type EfficientListTag
= EfficientList
type alias EfficientList =
Val Checked EfficientListTag
Internal { list : List a, length : Int }
serialize =
Serialize.map .list identity
|> Serialize.list
|> Val.serializeChecked EfficientList
(\list ->
{ list = list
, length = List.length length
}
|> Ok
)
A value is wrapped in the
type Valwith a phantomtag.A
Val ... Meters ... Floatcan't be called aVal ... Kilos ... Floatanymore!For
types with just 1 constructor with a value aValcan be a good replacement.who can construct such a value
CheckedTaggedwho can access the value
PublicInternalall promise additional type-safety.
reading types
Is saying:
mapworks on everyValand returns a value that is justTagged, but notChecked. ExplainingwhoCanAccess:PublicInternal,the result will be too.