sanctuary-js / sanctuary Goto Github PK
View Code? Open in Web Editor NEW:see_no_evil: Refuge from unsafe JavaScript
Home Page: https://sanctuary.js.org
License: MIT License
:see_no_evil: Refuge from unsafe JavaScript
Home Page: https://sanctuary.js.org
License: MIT License
Would it make sense to add a match
function?
R.head(R.match(/xxx/, 'abcde')) // returns TypeError: Cannot read property '0' of null
We'd be "safer" with something like
smatch = curry (re, s) ->
res = R.match re, s
return if res then Just(res) else Nothing()
Would it make sense to just add "safe" versions of all functions in Ramda that might otherwise need null checks? Is there some smart generic way to wrap Ramda functions and make them return Maybe
s instead?
As mentioned in #141, let's start try to get a list of typesafe functions we want from Ramda. I've put together a list of way too many functions that we can whittle down here as people chip in with their views.
R.append
R.concat
R.contains
R.equals
R.filter
R.fromPairs
R.keys
R.length
R.max
R.mean
R.median
R.merge
R.mergeAll
R.min
R.modulo
R.omit
R.prepend
R.product
R.range
R.reduce
R.reject
R.reverse
R.scan
R.split
R.sum
R.toPairs
R.trim
R.values
R.when
R.unless
R.zip
R.zipObj
Something I'm not clear on though is how we deal with functions that accept both arrays and string. Do we need a new type?
JavaScript is quite permissive:
> parseFloat('123abc')
123
> parseInt('123abc', 10)
123
Currently S.parseFloat
and S.parseInt
are straightforward wrappers for their built-in namesakes. As a result they're equally permissive. Should they be stricter? Is this possible without significantly increasing their complexity?
https://hackage.haskell.org/package/base-4.8.1.0/docs/Data-Maybe.html#v:mapMaybe:
The
mapMaybe
function is a version ofmap
which can throw out elements. In particular, the functional argument returns something of typeMaybe b
. If this isNothing
, no element is added on to the result list. If it isJust b
, thenb
is included in the result list.
Currently you can't compare two instances of Maybe
from different versions of Sanctuary
. If Sanctuary registers a Symbol for each type in the global symbol registry at require time then these symbols could be compared across different version numbers.
https://hackage.haskell.org/package/base-4.8.1.0/docs/Data-Maybe.html#v:catMaybes:
The
catMaybes
function takes a list ofMaybe
s and returns a list of all theJust
values.
Current behaviour:
> S.or(true, true, true)
TypeError: Expecting a function in instanceof check, but got undefined
Desired behaviour:
> S.or(true, true, true)
TypeError: ‘or’ requires two arguments; received three arguments
S.or(true)(true, true)
should behave the same way.
Sanctuary's run-time type checking currently relies on instanceof
checks. This guarantees that if a value passes type checking it will support all the expected operations.
If a function requires an argument of type Maybe a
, for example, a Maybe created by a different version of Sanctuary will fail type checking (with a rather confusing error message). For example:
TypeError: ‘fromMaybe’ requires a value of type Maybe as its second argument; received Just(42)
The problem with this approach is that it rejects valid values too. Requiring the same version of Sanctuary from two different paths results in two incompatible Sanctuary modules existing in memory:
const S1 = require('./path/to/sanctuary-0.7.0');
const S2 = require('./another/path/to/sanctuary-0.7.0');
S1.fromMaybe(NaN, S2.Just(42));
// ! TypeError: ‘fromMaybe’ requires a value of type Maybe as its second argument; received Just(42)
When might this occur?
In the browser, when dealing with iframes.
In Node, when using the vm module. @raine and I both discovered this while building REPLs.
In Node, when package-a
depends on both sanctuary
and package-b
, and package-b
also depends on sanctuary
. If package-a
applies a package-b
function which returns a Maybe, the Maybe will not be recognized by the sanctuary
module required by package-a
.
Note that npm performs dependency deduplication, so if package-a
and package-b
depend on the same version of sanctuary
, both will use package-a/node_modules/sanctuary. Relying on deduplication is undesirable: it's an optimization which should not effect a program's behaviour.
The first and second scenarios also affect built-in types such as Array. R.is
is also affected by this issue, as discussed in ramda/ramda#1140.
Clearly we must avoid instanceof
checks. What might we do instead? For built-in types we have Object.prototype.toString
and R.type
:
> Object.prototype.toString.call([])
'[object Array]'
> R.type([])
'Array'
For Sanctuary types (and other custom types) we need a different approach.
We can't use .constructor.name
because a type may have multiple constructors: Maybe has Nothing
and Just
, for example. Furthermore, there's no guarantee that two constructors with the same name are equivalent.
I propose a special property named '@@type'
or similar, with a reverse-DNS-ish value such as 'com.github.plaid.sanctuary/Maybe'
. The implementation would be straightforward:
Maybe.prototype['@@type'] = 'com.github.plaid.sanctuary/Maybe';
We could then write a function to determine the unique identifier for a value's type:
// typeName :: a -> String
var typeName = function(x) {
return x != null && R.type(x['@@type']) === 'String' ?
x['@@type'] :
'org.ecma-international.ecma-262/' + R.type(x);
};
typeName(Just(42));
// => 'com.github.plaid.sanctuary/Maybe'
typeName([1, 2, 3]);
// => 'org.ecma-international.ecma-262/Array'
Hat tip to @jethrolarson who has been giving this matter a lot of thought. I got the idea for a '@@type'
property from his xface project (though xface uses the property for a slightly different purpose).
Solving the problems associated with instanceof
checks is the most important issue I'd like to address in v0.8.0. I'd love to know what others think of this proposal. Counterproposals welcome!
Sanctuary is very cool.
However, what if a user wants to use sanctuary, but does not want the hard dependency on Ramda?
Is there a plan at some point to make this lib standalone?
This may or may not be useful:
> S.Int(7).add(S.Int(2))
Int(9)
> S.Int(7).divideBy(S.Int(2))
Int(3)
> S.Int(7).valueOf()
7
An integer type would allow us to specify types more precisely. S.at
, for example, could have type Int -> [a] -> Maybe a
.
parseJson(Array, '{}') -> Nothing()
I'm very fond of the get
and gets
style of type validation, because it forces you to do it. It would've already saved me on multiple occasions if parseJson
(or any x -> Any
function for that matter) had this built-in, because after doing a "safe" JSON parse, it doesn't always occur to me the return value may still be of an unwanted type.
An alternative is to extract this behaviour out to its own function, which is used solely for type validation:
validate :: TypeRep -> a -> Maybe a
const parseJsonArray = compose(chain(validate(Array)), parseJson);
I think this second solution is more elegant in terms of modularity but it lacks the safety that comes from forcing you to do the type validation.
Side note: TypeRep
may be changed to a predicate, but the idea remains the same.
So I have a cycle.js project, I use ramda's pipe
, things work fine:
Today I wrote a PR that switches this project to using sanctuary, but ran into some really head scratching issues:
The essence of this issue can be observed by modifying
https://github.com/lacqueristas/www/pull/1/files#diff-895656aeaccff5d7c0f56a113ede9662L37
to look like this
const dom$ = map((value) => console.log(value), storage$)
It now will console a strange Array: [Object, 0, MapObservable]
. Before the change if you did this you would only get Object
.
Since the entire pull request is simply importing from sanctuary and switching to the pipe([])
api...I'm left wondering why this broke?
This was suggested by @Bpless as a way to extract the b
from a value of type Either a b
. It's analogous to fromMaybe :: a -> Maybe a -> a
.
Usage:
> S.fromEither(NaN, S.Left('XXX'))
NaN
> S.fromEither(NaN, S.Right(42))
42
Should we enable this feature to save contributors constantly having to squash and rebase their commits into 1?
For example:
> S.Just(2).concat(S.Just(2))
TypeError: undefined is not a function
It would be nice to see something this instead:
> S.Just(2).concat(S.Just(2))
TypeError: ‘Maybe#concat’ requires a semigroup as its first argument; received Just(2)
Let's explore if these functions in data.either
would be useful in Sanctuary:
@scott-christopher defined Foldable for Maybe in #69. It would be useful to define Traversable as well, particularly in light of ramda/ramda#1467.
Sanctuary strives to complement Ramda with safe, total functions providing strong guarantees about their behavior. Enforcement is via first-order type checking, e.g. throwing TypeError when given a Number where a String is expected. However, higher-order properties are more difficult to enforce - how does one verify that the function given to Maybe.filter
returns a boolean? You can check that the given value is a function but that's not enough, you'd have to check that it returns a boolean every time it's called in the implementation of Maybe.filter
.
Contracts are designed to solve this problem:
import @ from "contracts.js"
@ ((Num) -> Num) -> Num
function runNumberFunc(numberFunc) {
return numberFunc(10) + 1;
}
runNumberFunc(x => 2 * x); // valid, produces 21
runNumberFunc("foo"); // first order error, didn't provide a function
runNumberFunc(x => "foo"); // higher order error, function returned the wrong result
And just look at the error messages!
> runNumberFunc("foo");
Error: runNumberFunc: contract violation
expected: a function that takes 1 argument
given: 'foo'
in: the 1st argument of
((Num) -> Num) -> Num
function runNumberFunc guarded at line: 3
blaming: (calling context for runNumberFunc)
> runNumberFunc(x => "foo");
Error: runNumberFunc: contract violation
expected: Num
given: 'foo'
in: the return of
the 1st argument of
((Num) -> Num) -> Num
function runNumberFunc guarded at line: 3
blaming: (calling context for runNumberFunc)
That funny looking @ ...
line, as careful readers might note, isn't valid javascript. It's a sweet.js
macro, a rule for transforming code from arbitrary syntax into valid javascript. The Sweet JS macro and build system provides a framework for defining these macros, and tools to generate "normal" javascript from "sweet" javascript. Using these contracts in Sanctuary would therefore imply some nontrivial wrangling of its current build system and release process, but it would be invisible to clients of the library. I would argue it falls well within the philosophy of Sanctuary to provide this.
Maps are a new feature of ES6 that could replace the de facto use of objects as dictionaries in JS.
Haskell has a Data.Map module that has the following API for getting a value for a given key.
lookup :: Ord k => k -> Map k a -> Maybe a
This feels like a pretty natural fit for sanctuary and mimics the way we're using S.get
in a lot of places currently. How would you feel about adding some functions that explicitly operate on ES6 Maps with a nice API using the Maybe type?
I am missing the pointfree chain
function in this library.
I have several functions of signature Functor f => a -> f a
, which I would like to compose using pipe
.
I have written the following conversion to pointfree of chain
to allow this.
// Functor f => (a -> f a) -> f a -> f a
const chain = func => x => x.chain(func);
Now I can write
// Functor f => a -> f a
const composed = pipe([func1, chain(func2), chain(func3)]);
Maybe there is a different intended way of doing this with this library? If not, I think the simple chain
function would be a good addition.
Similar to R.cond
but stricter and simpler (see ramda/ramda#1673). I imagine R.cond
is used with unary functions the vast majority of the time.
Since most type-checking libraries provide convenient ways to get the predicate for a type, this would allow for much greater interoperability with run-time type systems.
With Sanctuary:
const isInt = allPass([is(Number), x => x % 1 === 0]);
get(isInt, 'x', {x: 0.5}); //-> Nothing
With TComb:
const Int = t.refinement(t.Number, x => x % 1 === 0, 'Int');
get(Int.is, 'x', {x: 0.5}); //-> Nothing
With Sanctuary Def:
const Int = $.NullaryType('my/Int', allPass([is(Number), x => x % 1 === 0]));
get(Int.test, 'x', {x: 0.5}); //-> Nothing
The possibilities are endless! Another option is to accept Sanctuary Def types, which would make the code a little more concise, but the possibilities slightly less endless.
@Avaq raised the issue of combinators on Gitter. Sanctuary currently exports I
and K
, but fantasy-combinators defines several more, neatly summarized in a gist:
const I = x => x; const K = x => _ => x; const A = f => x => f(x); const T = x => f => f(x); const C = f => y => x => f(x)(y); const B = f => g => x => f(g(x)); const S = f => g => x => f(x)(g(x)); const P = f => g => x => y => f(g(x))(g(y)); const Y = f => (g => g(g))(g => f(x => g(g)(x)));
A
is a strict version of R.call
(since it expects a unary function).
C
is a strict version of S.flip
(since it expects a curried function).
B
is S.compose
.
I'd like Sanctuary to provide more of these combinators. I'd welcome a pull request for one or more of these functions. :)
It would be nice to have type-safe versions of R.add
, R.subtract
, R.multiply
and R.divide
. If people think that's a good idea, I will do a PR once #123 has landed.
We should define sanctified versions of R.takeLast
and R.dropLast
.
If you're interested in contributing to Sanctuary for the first time, this is a well-defined, self-contained problem well-suited to a first contribution. :)
It would be helpful with a table of contents in the readme docs. I tried generating one with DocToc, but it choked on the markup of the function signatures.
Before I try to work around it – are you interested in getting a PR for it at all?
This has always felt awkward in Ramda since the supplied function returns false
or an actual value. The haskell version uses maybe and is much more elegant.
(b -> Maybe (a, b)) -> b -> [a]
I started work on the maths functions listed in #127 but have encountered some really strange behaviour. The very act of including $.FiniteNumber
in $.env
causes a bunch of tests to fail. They all involve functions that have a dependency on type classes in their definition. Here are some of the errors:
TypeError: ‘Either#concat’ requires ‘a’ to implement Semigroup; Number and Integer and FiniteNumber do not
TypeError: ‘xor’ requires ‘a’ to implement Alternative and Monoid; (Either ??? Number) and (Either ??? Integer) and (Either ??? FiniteNumber) do not
TypeError: ‘xor’ requires ‘a’ to implement Alternative and Monoid; Number and Integer and FiniteNumber do not
Etc...
I'm guessing there's something going on with the definition of Sanctuary's Semigroup
and Alternative
types and is related to sanctuary-js/sanctuary-def/pull/16
Let's consider a problem which a hypothetical do
function might help us to solve:
Given an object, return the square root of the number represented by the string at path
a.b.c
. Do not assume, however, that the patha.b.c
will exist.
Here's the obvious naive solution:
// naive :: Object -> Number
const naive = o => Math.sqrt(parseFloat(o.a.b.c));
This won't handle all the following inputs:
{a: {b: {c: '2.25'}}}
{a: {b: {c: 'blah'}}}
{a: {b: {}}}
{a: {}}
{}
This is how I would solve this problem today:
// safe1 :: Object -> Maybe Number
const safe1 = R.pipe(
S.get('a'),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.map(Math.sqrt)
);
With two small changes we can make this more uniform:
// safe2 :: Object -> Maybe Number
const safe2 = R.pipe(
S.Just,
R.chain(S.get('a')),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.chain(R.compose(S.Just, Math.sqrt))
);
If we then changed the type from Object -> Maybe Number
to Maybe Object -> Maybe Number
we could remove the first step of the pipeline:
// safe3 :: Maybe Object -> Maybe Number
const safe3 = R.pipe(
R.chain(S.get('a')),
R.chain(S.get('b')),
R.chain(S.get('c')),
R.chain(S.parseFloat),
R.chain(R.compose(S.Just, Math.sqrt))
);
Now we have a form which can surely be abstracted! Given a list of functions we want to apply R.chain
to each, then apply R.pipe
to the list as positional arguments:
// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z
const $do = fs => R.apply(R.pipe, R.map(R.chain, fs));
This can be written point-free:
// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z
const $do = R.pipe(R.map(R.chain), R.apply(R.pipe));
Let's rewrite safe3
in terms of $do
:
// safe4 :: Maybe Object -> Maybe Number
const safe4 = $do([
S.get('a'),
S.get('b'),
S.get('c'),
S.parseFloat,
R.compose(S.Just, Math.sqrt),
]);
$do
works for any Monad, and should reduce demand for "composed-chain" functions such as S.gets
.
What do you think? Shall we add this? If so, what should it be named? S.do
is problematic since do
is not a valid identifier. S.$do
is pretty ugly. Does do
go by other names?
> R.empty(S.Nothing())
Nothing()
> R.empty(S.Just([1, 2, 3]))
Nothing()
Should the latter give Just([])
instead? Monoids for Maybe suggests that both approaches are valid. Which do you think is more useful, @joneshf?
In Ramda we're slowly replacing variadic functions with fixed-arity functions. I'm very much in favour of this. I'd like to go a step further in Sanctuary by fixing the arities of function arguments. R.both
, for example, has type (** -> Boolean) -> (** -> Boolean) -> (** -> Boolean)
, where **
represents zero or more arguments. I'd like to provide a both
function in Sanctuary which is less flexible but which handles the common case with a simpler type, (a -> Boolean) -> (a -> Boolean) -> a -> Boolean
.
The following is a list of such functions I'd like Sanctuary to provide (which I'll update it if turns out not to be exhaustive).
both :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean
either :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean
anyPass :: [a -> Boolean] -> a -> Boolean
allPass :: [a -> Boolean] -> a -> Boolean
complement :: (a -> Boolean) -> a -> Boolean
ifElse :: (a -> Boolean) -> (a -> b) -> (a -> b) -> a -> b
converge :: (b -> c -> d) -> (a -> b) -> (a -> c) -> a -> d
flip :: (a -> b -> c) -> b -> a -> c
This approach was briefly discussed in ramda/ramda#1395.
Problem:
S.I(['foo', 42]);
// ! TypeError: Type-variable constraint violation
//
// I :: a -> a
// ^
// 1
//
// 1) ["foo", 42] :: Array ???
//
// Since there is no type of which all the above values are members, the type-variable constraint has been violated.
If this is a case of array-as-list, a type error is appropriate. But perhaps it's a case of array-as-tuple?
sanctuary-def could—and should—provide Pair a b
to support two-element arrays. The fact remains, though, that Sanctuary's environment cannot possibly include every type, as there are infinitely many.
Solution:
The user must have the ability to provide her own environment.
"Initializing" Sanctuary is currently done in one of two ways:
const S = require('sanctuary');
const S = require('sanctuary').unchecked;
If the user is to provide an environment, it makes sense to take (checkTypes :: Boolean, env :: [Type])
as $.create
does:
const sanctuary = require('sanctuary');
const S = sanctuary.create(true, sanctuary.env);
const sanctuary = require('sanctuary');
const S = sanctuary.create(false, sanctuary.env);
When @Avaq and I discussed this on Gitter, he suggested having const S = require('sanctuary');
continue to work as it does today. This strikes me as a great idea, as it would allow new users to have a positive first experience with the library.
Please check ahead of time when declaring pipe statements if the functions are defined.
Currently stack traces when a piped function is undefined are very obscure.
Using version 0.7.1.
import {pipe} from 'sanctuary';
const samplePackage = {};
const breakingFunction = pipe([samplePackage.doesNotExist]);
breakingFunction("Hello world");
Outputs
TypeError: g is not a function
at /.../node_modules/sanctuary/index.js:257:16
at /.../node_modules/sanctuary/index.js:395:42
at /.../node_modules/sanctuary/index.js:246:48
at /.../node_modules/sanctuary/index.js:179:45
at Object.<anonymous> (/.../src/index.js:4:1)
As suggested by @ChetHarrison on Gitter.
bower register sanctuary git://github.com/plaid/sanctuary.git
R.divide
isn't actually unsafe, but Infinity
is as unhelpful as NaN
in many situations.
div :: Number -> Number -> Maybe Number
div a 0 = Nothing
div a b = Just (a / b)
Working on #38 got me thinking about the other Ramda string functions that return a negative result that's not necessarily a null
or undefined
. For example, if you pass an index to substringFrom
that's greater than the length of the string being operated on you just get back an empty string. Would it be useful to have a sanctified version that returns a Nothing in these sorts of cases?
R.lensProp
and R.lensIndex
aren't quite lawful, as @scott-christopher explained in ramda/ramda#1281 (comment).
Scott, have you any interest in working on Sanctuary equivalents of Ramda's lens functions, which would use the Maybe type where appropriate to satisfy the lens laws?
Similar to regex
from PureScript's Data.String.Regex module.
https://hackage.haskell.org/package/base-4.8.1.0/docs/Data-Maybe.html#v:maybe:
The
maybe
function takes a default value, a function, and aMaybe
value. If theMaybe
value isNothing
, the function returns the default value. Otherwise, it applies the function to the value inside theJust
and returns the result.
Would you be interested in adding a helper function for a random number generator, S.random
?
Math.random
is limited in that you can't specify a range (it's [0, 1)
), which is why Lodash and Underscore have their own implementation. It would be nice to have something like this in the library.
This is really sheer laziness on my part but my code is littered with R.map(S.get('key'), arr))
, it's an improvement on having to remember to do R.reject(R.isNil, R.pluck('key', arr))
whenever I use R.pluck
but it would be nice to have some sugar around such a common pattern.
Is there space in sanctuary for a reduce-like function that calls the reducing function once on the accumulator and then the result on the current element? As it stands, Sanctuary, like in Ramda, doesn't work for manually curried functions.
Would it be reasonable for get
to have the above behaviour? I'd like to use this function as the 'gateway' for converting the values in a function pipeline to Maybe
s. Currently I'm a querying a datasource using highland
for some JSON that may return null
. At the moment I do this:
var _ = require('highland');
var R = require('ramda');
var S = require('sanctuary');
_(datasource)
.map(R.compose(R.chain(S.get('b')), R.chain(S.get('a')), S.toMaybe))
.reject(S.Nothing().equals)
It would be nice not to have to do that initial map with toMaybe
and the subsequent call to R.chain so it would look like this:
_(datasource)
.map(R.compose(R.chain(S.get('b')), S.get('a')))
.reject(S.Nothing().equals)
Or even nicer once v0.5.0 of Sanctuary drops:
_(datasource)
.map(S.gets(['a', 'b']))
.reject(S.Nothing().equals)
#123 had one major unforeseen consequence.
To determine whether a value is a member of Number
, say, sanctuary-def can ask $.Number.test(x)
? To determine whether a value is a member of a
, a type variable, sanctuary-def filters the set of types in the current environment to determine the set of types of which the value is a member, constraining a
.
The problem is that Sanctuary provides a list of types to $.create
, essentially stating these are all the types that exist. Sanctuary's polymorphic functions cannot operate on "unknown" types:
S.I(document.body);
// ! TypeError: ‘I’ received [object HTMLBodyElement] as its first argument, but this value is not a member of any of the types in the environment:
//
// - (Array ???)
// - Boolean
// ...
// - ValidDate
// - ValidNumber
S.I({'@@type': 'my-package/MyType'});
// ! TypeError: ‘I’ received {"@@type": "my-package/MyType"} as its first argument, but this value is not a member of any of the types in the environment:
//
// - (Array ???)
// - Boolean
// ...
// - ValidDate
// - ValidNumber
This is clearly a problem. What's more, S.type
handles these "foreign" values:
> S.type(document.body)
'HTMLBodyElement'
> S.type({'@@type': 'my-package/MyType'})
'my-package/MyType'
It's trivial to compare two type identifiers to determine whether two values are of the same type.
I believe the solution is as follows. When sanctuary-def determines that the value under interrogation is not a member of any of the types in the environment, it should not despair (as it currently does). Instead, it should calmly create a NullaryType
and add it to the map:
var typeIdentifier = $$type(value);
$typeVarMap[typeVarName] =
[$.NullaryType(typeIdentifier, $$typeEq(typeIdentifier))];
It seems the fix should not be difficult. I hope to release a new version of sanctuary-def and a new version of Sanctuary this weekend.
Thank you @raqystyle for raising this issue on Gitter.
This function could be used by other libraries. It would also enable wrappers for libraries (such as Ramda) which don't provide a descriptive error message when given a value of the wrong type.
Currently, encase
and encaseEither
have misleading type signatures:
encase :: (* -> a) -> (* -> Maybe a)
encaseEither :: (Error -> a) -> (* -> b) -> (* -> Either a b)
They are misleading because Hindley–Milner notation cannot represent a function which returns a curried function of unknown arity.
In addition to the documentation problem, there's a usability problem. Functions with unwanted optional arguments must be wrapped in lambdas. For example:
S.encaseEither(S.I, x => JSON.parse(x))
A solution to both problems is to have one function for each arity up to some arbitrary limit:
encase :: (a -> b) -> a -> Maybe b
encase2 :: (a -> b -> c) -> a -> b -> Maybe c
encase3 :: (a -> b -> c -> d) -> a -> b -> c -> Maybe d
encaseEither :: (Error -> l) -> (a -> r) -> a -> Either l r
encaseEither2 :: (Error -> l) -> (a -> b -> r) -> a -> b -> Either l r
encaseEither3 :: (Error -> l) -> (a -> b -> c -> r) -> a -> b -> c -> Either l r
An incidental benefit of this approach is that arguments to the wrapped function may be provided up front. For example:
> S.encaseEither(R.prop('message'), JSON.parse, '[1,2,3]')
Right([1, 2, 3])
> S.encaseEither(R.prop('message'), JSON.parse, 'XXX')
Left("Unexpected token X")
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.