-
Notifications
You must be signed in to change notification settings - Fork 14
Remove the IsOption type class #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| } | ||
|
|
||
| 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)] | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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
Optionknows the key already.There was a problem hiding this comment.
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.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using
Opfromcontravariant. Then you get some nice instances.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Such as these https://github.com/purescript/purescript-contravariant/blob/master/docs/Data/Functor/Contravariant/Divisible.md
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 typeEithervia tags, for example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keywas added in 384219d. I really can't recall a specific reason for adding this. I am open to removing it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, @garyb does adding
keyring any bells for you by chance? I can't recall why I made that commit.There was a problem hiding this comment.
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-optionsso 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.There was a problem hiding this comment.
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-
Optionrelationship, keepingkey, and to add theOpversion as an alternative for more complex scenarios.