A data type for untagged unions.
Consider a the following type:
import Untagged.Union
type ISB = Int |+| String |+| BooleanThe type ISB describes values which can either be an Int, a String or a Boolean. Moreover, it is guaranteed that at runtime, values of type ISB is directly stored as an Int, a String or a Boolean without any wrappers. This makes it especially useful for FFI.
Note that |+| is an alias for OneOf.
In order to create a value of OneOf, use asOneOf.
isb1 :: ISB
isb1 = asOneOf 20
isb2 :: ISB
isb2 = asOneOf "foo"
--isb3 :: ISB
--isb3 = asOneOf 3.5
-- isb3 will fail since 3.5 is a Number which is neither
-- an Int, String nor Boolean
The library also defines Undefined. Combined with OneOf, it can represent an optional type:
import Literals.Undefined
type OptionalInt = Undefined |+| IntAn alias UndefinedOr is also provided.
type OptionalInt' = UndefinedOr Int -- Same as `Undefined |+| Int`To get a purescript value back, use fromOneOf:
import Data.Maybe
valInt :: Maybe Int
valInt = fromOneOf isb1 -- evaluates to `Just 20`
valString :: Maybe String
valString = fromOneOf isb1 -- evalutes to `Nothing` since isb1 contains an Int
Using OneOf, describing JS types as records becomes intuitive:
type Props =
{ text :: String -- a required field
-- Optional Fields
, width :: UndefinedOr Number
, height :: UndefinedOr Number
-- Optional and Varying types
, fontSize :: Undefined |+| String |+| Number
}A cast helper is made available to convert records with the same runtime value:
import Untagged.Castable (cast)
sampleProps :: Props
sampleProps =
cast { text: "foo" -- text is required and should be a string
, width: 30.0 -- width is optional, and may be defined, but should be a Number
-- height is optional and may be omitted
, fontSize: "100%" -- fontSize may be defined, and should either be a string or number
}
Nesting is possible but requires some hints for the compiler.
Consider the following:
type Props =
{ text :: String -- a required field
, width :: UndefinedOr Number
, height :: UndefinedOr Number
, fontSize :: Undefined |+| String |+| Number
}
type ContainerProps =
{ titleProps :: UndefinedOr Props
, opacity :: UndefinedOr Number
}In order to create an instance of ContainerProps, the type of titleProps must be specified.
sampleContainerProps :: ContainerProps
sampleContainerProps =
cast { titleProps: (cast { text: "Foo" } :: Props)
}Alternatively, helper constructors may be used to aid type inference.
import Untagged.Castable (Castable)
props :: forall r. Castable r Props => r -> Props
props = cast
containerProps :: forall r. Castable r ContainerProps => r -> ContainerProps
containerProps = cast
sampleContainerProps' :: ContainerProps
sampleContainerProps' =
containerProps { titleProps: props { text: "Foo" }
}