Data.Options
- Package
- purescript-options
- Repository
- purescript-contrib/purescript-options
Overview of the Problem
This module provides a way of dealing with the JS idiom of options objects in PureScript, by giving you the tools to provide a reasonably comfortable typed layer on top of JavaScript APIs which make use of this idiom.
Many JavaScript APIs include functions which take an object argument,
where the object's properties come from a fixed set of optional
configuration values. For example, the createWriteStream
function from
the Node.js fs
module may contain properties such as:
flags
, which should be aString
, such as"w"
,"rw"
, or"r+"
,defaultEncoding
, which should be aString
representing an encoding, such as"utf8"
,
and so on.
Why PureScript Records Don't Work
PureScript's record types can be a little awkward for this, since it is
usually the case that any subset of these properties can be specified;
however, a value of type { flags :: String, defaultEncoding :: String, [...] }
must include every property listed, even if you only want to specify
one or two properties.
Using this Library
This library provides a better solution for this problem. Using this
module, you could wrap fs.createWriteStream
as follows:
First, create a phantom type used for the options object:
data CreateWriteStreamOptions
Then, create Option
values for each of the options:
flags :: Option CreateWriteStreamOptions String
flags = opt "flags"
defaultEncoding :: Option CreateWriteStreamOptions String
defaultEncoding = opt "defaultEncoding"
-- and so on
Import the function you're wrapping using the FFI, using the Foreign
type for the options object:
-- don't export this!
foreign import createWriteStreamImpl :: FilePath -> Foreign -> Effect Unit
Finally, in the function you are going to export, take an Options
value
for the options argument, and use the options
function provided by this
library to convert it into a Foreign
value, which will then have a
suitable representation for passing to the JavaScript API.
createWriteStream :: forall eff. FilePath -> Options CreateWriteStreamOptions -> Effect Unit
createWriteStream path opts = createWriteStreamImpl path (options opts)
Then, users of your API can create Options
values using the :=
operator to assign values for the options they want to configure, and the
Monoid Options
instance to combine them. For example, as a user of this
API, you might write:
FS.createWriteStream "file.txt" $
defaultEncoding := "utf8" <>
flags := "rw"
Increasing Type Safety
You can also use more specific types for more type safety. For example,
it would be safer to use the existing FileFlags
and Encoding
types
already provided by the node-fs
library. However, we cannot use them
directly because they will have the wrong runtime representation. This is
where the Contravariant
instance for Option
comes in; it can be used
to transform an option's value to give it a suitable runtime
representation based on what the JS API is expecting. For example:
flags :: Option CreateWriteStreamOptions FileFlags
flags = cmap fileFlagsToNode (opt "flags")
Note that fileFlagsToNode
takes a FileFlags
and returns a String
suitable for passing to a Node.js API.
#(:=) Source
Operator alias for Data.Options.assoc (right-associative / precedence 6)
An infix version of assoc
.
#defaultToOptions Source
defaultToOptions :: forall opt value. String -> value -> Options opt
The default method for turning a string property key into an
Option
. This function simply calls unsafeToForeign
on the value. If
you need some other behaviour, you can write your own function to replace
this one, and construct an Option
yourself.
- Modules
- Data.
Options