For example, if you wanted to provide a low-level typing for the XMLHttpRequest web API, you might write the following:
type XMLHttpRequest e = EffObject -- The first type argument contains effects associated with interacting -- with the object. Interacting with an XHR uses an AJAX effect, so -- we record this here. ( ajax :: AJAX ) -- We record the properties as well as their types and access levels -- in the second type argument. ( onreadystatechange :: ReadWrite (Eff e Unit) , readyState :: ReadOnly Int , response :: ReadOnly (Nullable Response) , [...] )
Consumers of your API can now interact with an
checkReadyState :: forall e. XMLHttpRequest e -> Eff (ajax :: AJAX | e) String checkReadyState req = do s <- readProperty (SProxy :: SProxy "readyState") req pure $ if (s == done) then "Done!" else "Not done." where done = 4
Attempting to write read-only properties will produce a custom type error explaining what happened:
oops :: forall e. XMLHttpRequest e -> Eff (ajax :: AJAX | e) Unit oops req = writeProperty (SProxy :: SProxy "readyState") req 0 -- Throws: -- A custom type error occurred while solving type class constraints: -- Cannot write to a read-only property.
Note that any APIs constructed using this library will necessarily be
very low-level; you might want to build extra layers above this API, for
instance, to provide a real
ReadyState type which is guaranteed to only
take values which would be valid the for
readyState property itself.
e: Effects associated with reading or writing properties
The labels in the
props row type must correspond exactly to the property
names on the underlying object, and the types in the
props row should
be of the form
WriteOnly a, or
ReadWrite a, depending
on the access level, and where
a is the type of the property's value.
This type provides a way of saying that a
BoundFunction which is a
property of some
EffObject should receive that
EffObject as its
value when called.