Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
],
"dependencies": {
"purescript-foreign": "~0.7.0",
"purescript-unsafe-coerce": "~0.1.0"
"purescript-maps": "~0.5.4",
"purescript-tuples": "~0.4.0",
"purescript-monoid": "~0.3.2",
"purescript-maybe": "~0.3.5",
"purescript-contravariant": "~0.2.3"
},
"devDependencies": {
"purescript-console": "~0.1.1"
Expand Down
89 changes: 62 additions & 27 deletions docs/Data/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,112 @@
#### `Options`

``` purescript
data Options :: * -> *
newtype Options opt
```

The `Options` type represents a set of options. The type argument is a
phantom type, which is useful for ensuring that options for one particular
API are not accidentally passed to some other API.

##### Instances
``` purescript
instance semigroupOptions :: Semigroup (Options a)
instance monoidOptions :: Monoid (Options a)
Semigroup (Options opt)
Monoid (Options opt)
```

#### `Option`
#### `runOptions`

``` purescript
data Option :: * -> * -> *
runOptions :: forall opt. Options opt -> Array (Tuple String Foreign)
```

#### `IsOption`
#### `options`

``` purescript
class IsOption value where
assoc :: forall opt. Option opt value -> value -> Options opt
options :: forall opt. Options opt -> Foreign
```

Convert an `Options` value into a JavaScript object, suitable for passing
to JavaScript APIs.

#### `Option`

``` purescript
newtype Option opt value
= Option { key :: String, toOptions :: String -> value -> Options opt }
```

An `Option` represents an opportunity to configure a specific attribute
of a call to some API. This normally corresponds to one specific property
of an "options" object in JavaScript APIs.

##### Instances
``` purescript
instance isOptionString :: IsOption String
instance isOptionBoolean :: IsOption Boolean
instance isOptionNumber :: IsOption Number
instance isOptionInt :: IsOption Int
instance isOptionRecord :: IsOption { | a }
instance isOptionUnit :: IsOption Unit
instance isOptionFunction :: IsOption (a -> b)
instance isOptionArray :: (IsOption a) => IsOption (Array a)
instance isOptionMaybe :: (IsOption a) => IsOption (Maybe a)
Contravariant (Option opt)
```

#### `assoc`

``` purescript
assoc :: forall opt value. Option opt value -> value -> Options opt
```

Associates a value with a specific option.

#### `(:=)`

``` purescript
(:=) :: forall opt value. (IsOption value) => Option opt value -> value -> Options opt
(:=) :: forall opt value. Option opt value -> value -> Options opt
```

_right-associative / precedence 6_

#### `optionFn`
An infix version of `assoc`.

#### `massoc`

``` purescript
optionFn :: forall opt from to. Option opt from -> Option opt to
massoc :: forall opt value. Option opt value -> Maybe value -> Options opt
```

#### `key`
A version of `assoc` which takes possibly absent values. `Nothing` values
are ignored; passing `Nothing` for the second argument will result in an
empty `Options`.

#### `(:=?)`

``` purescript
key :: forall opt value. Option opt value -> String
(:=?) :: forall opt value. Option opt value -> Maybe value -> Options opt
```

_right-associative / precedence 6_

#### `opt`

``` purescript
opt :: forall opt value. (IsOption value) => String -> Option opt value
opt :: forall opt value. String -> Option opt value
```

#### `runOptions`
The default way of creating `Option` values. Constructs an `Option` with
the given key, which passes the given value through unchanged.

#### `key`

``` purescript
runOptions :: forall a. Options a -> Array (Tuple String Foreign)
key :: forall opt value. Option opt value -> String
```

#### `options`
Get the `String` key of an `Option`.

#### `defaultToOptions`

``` purescript
options :: forall a. Options a -> Foreign
defaultToOptions :: forall opt value. String -> value -> Options opt
```

The default method for turning a string property key and a value into an
`Options` value. This function simply calls `toForeign` on the value. If
you need some other behaviour, you can write your own function to replace
this one, and construct an `Option` yourself.


63 changes: 0 additions & 63 deletions src/Data/Options.js

This file was deleted.

131 changes: 67 additions & 64 deletions src/Data/Options.purs
Original file line number Diff line number Diff line change
@@ -1,94 +1,97 @@
module Data.Options
( Options()
, Option()
, IsOption
, assoc, (:=)
, optionFn
, options
, runOptions
, options
, Option(..)
, assoc, (:=)
, massoc, (:=?)
, opt, key
, defaultToOptions
) where

import Prelude

import Data.Foreign (Foreign())

import Data.Function (Fn2(), runFn2)
import Data.Foreign (toForeign, Foreign())

import Data.Maybe (Maybe(..))
import Data.Maybe (Maybe(), maybe)

import Data.Monoid (Monoid)
import Data.Monoid (mempty, Monoid)

import Data.Tuple (Tuple(..))

import Unsafe.Coerce (unsafeCoerce)
import Data.StrMap as StrMap

foreign import data Options :: * -> *
import Data.Functor.Contravariant (Contravariant)

foreign import data Option :: * -> * -> *
-- | The `Options` type represents a set of options. The type argument is a
-- | phantom type, which is useful for ensuring that options for one particular
-- | API are not accidentally passed to some other API.
newtype Options opt =
Options (Array (Tuple String Foreign))

class IsOption value where
assoc :: forall opt. Option opt value -> value -> Options opt
runOptions :: forall opt. Options opt -> Array (Tuple String Foreign)
runOptions (Options xs) = xs

infixr 6 :=

(:=) :: forall opt value. (IsOption value) => Option opt value -> value -> Options opt
(:=) = assoc
instance semigroupOptions :: Semigroup (Options opt) where
append (Options xs) (Options ys) = Options (xs <> ys)

instance semigroupOptions :: Semigroup (Options a) where
append = runFn2 appendFn
instance monoidOptions :: Monoid (Options opt) where
mempty = Options []

instance monoidOptions :: Monoid (Options a) where
mempty = memptyFn
-- | Convert an `Options` value into a JavaScript object, suitable for passing
-- | to JavaScript APIs.
options :: forall opt. Options opt -> Foreign
options = toForeign <<< StrMap.fromFoldable <<< runOptions

instance isOptionString :: IsOption String where
assoc = runFn2 isOptionPrimFn
-- | An `Option` represents an opportunity to configure a specific attribute
-- | of a call to some API. This normally corresponds to one specific property
-- | of an "options" object in JavaScript APIs.
newtype Option opt value =
Option
{ key :: String
, toOptions :: String -> value -> Options opt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the key need to be passed here? Surely the Option knows the key already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I'll see if I can change this to just value -> Options opt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using Op from contravariant. Then you get some nice instances.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is kind of a perfect way to compose options, providing sets of options which can be set at once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't need it, I think we should get rid, since we buy the opportunity to represent multiple keys with one Option, which can be very useful when providing an idiomatic interface to things of type Either via tags, for example.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key was added in 384219d. I really can't recall a specific reason for adding this. I am open to removing it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, @garyb does adding key ring any bells for you by chance? I can't recall why I made that commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry! I didn't seem to get the notification on this. I don't really recall anything specifically to do with key, but it's been a while since I made use of -options so it's a little hard to remember.

I've been trying to think where I might have used it, other than tweaking things in gulp-purescript, I feel like there is somewhere I'm forgetting about where I wrote some stuff from scratch and needed some assistance to get it to do what I wanted, but I have no idea what it was now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to keep the one-key-to-one-Option relationship, keeping key, and to add the Op version as an alternative for more complex scenarios.

}

instance isOptionBoolean :: IsOption Boolean where
assoc = runFn2 isOptionPrimFn
instance contravariantOption :: Contravariant (Option opt) where
cmap f (Option o) =
Option
{ key: o.key
, toOptions: \k v -> o.toOptions k (f v)
}

instance isOptionNumber :: IsOption Number where
assoc = runFn2 isOptionPrimFn
-- | Associates a value with a specific option.
assoc :: forall opt value. Option opt value -> value -> Options opt
assoc (Option o) value = o.toOptions o.key value

instance isOptionInt :: IsOption Int where
assoc = runFn2 isOptionPrimFn

instance isOptionRecord :: IsOption { | a } where
assoc = runFn2 isOptionPrimFn
infixr 6 :=

instance isOptionUnit :: IsOption Unit where
assoc = runFn2 isOptionPrimFn
-- | An infix version of `assoc`.
(:=) :: forall opt value. Option opt value -> value -> Options opt
(:=) = assoc

instance isOptionFunction :: IsOption (a -> b) where
assoc = runFn2 isOptionPrimFn
-- | A version of `assoc` which takes possibly absent values. `Nothing` values
-- | are ignored; passing `Nothing` for the second argument will result in an
-- | empty `Options`.
massoc :: forall opt value. Option opt value -> Maybe value -> Options opt
massoc option = maybe mempty (option :=)

instance isOptionArray :: (IsOption a) => IsOption (Array a) where
assoc k vs = joinFn $ assoc (optionFn k) <$> vs
infixr 6 :=?

instance isOptionMaybe :: (IsOption a) => IsOption (Maybe a) where
assoc k Nothing = memptyFn
assoc k (Just a) = assoc (optionFn k) a
(:=?) :: forall opt value. Option opt value -> Maybe value -> Options opt
(:=?) = massoc

optionFn :: forall opt from to. Option opt from -> Option opt to
optionFn = unsafeCoerce
-- | The default way of creating `Option` values. Constructs an `Option` with
-- | the given key, which passes the given value through unchanged.
opt :: forall opt value. String -> Option opt value
opt k = Option { key: k, toOptions: defaultToOptions }

-- | Get the `String` key of an `Option`.
key :: forall opt value. Option opt value -> String
key = unsafeCoerce

opt :: forall opt value. (IsOption value) => String -> Option opt value
opt = unsafeCoerce

runOptions :: forall a. Options a -> Array (Tuple String Foreign)
runOptions = runOptionsFn Tuple

foreign import memptyFn :: forall a. Options a

foreign import appendFn :: forall a. Fn2 (Options a) (Options a) (Options a)

foreign import joinFn :: forall a b. Array (Options a) -> Options b

foreign import isOptionPrimFn :: forall b a. Fn2 (Option b a) a (Options b)

foreign import options :: forall a. Options a -> Foreign

foreign import runOptionsFn :: forall a. (String -> Foreign -> Tuple String Foreign) -> Options a -> Array (Tuple String Foreign)
key (Option o) = o.key

-- | The default method for turning a string property key and a value into an
-- | `Options` value. This function simply calls `toForeign` on the value. If
-- | you need some other behaviour, you can write your own function to replace
-- | this one, and construct an `Option` yourself.
defaultToOptions :: forall opt value. String -> value -> Options opt
defaultToOptions k v = Options [Tuple k (toForeign v)]