Giter Site home page Giter Site logo

sanctuary's People

Contributors

avaq avatar benperez avatar bradcomp avatar crisfeo avatar cust0dian avatar danielo515 avatar davidchambers avatar delapouite avatar farobi avatar futpib avatar gabejohnson avatar gilligan avatar jacse avatar jaforbes avatar jdudek avatar jordrake avatar kedashoe avatar laduke avatar lucasschejtman avatar masaeedu avatar pranavvishnu avatar qm3ster avatar rjmk avatar sadasant avatar samueller avatar shard avatar stephanschubert avatar svozza avatar syves avatar wennergr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sanctuary's Issues

remove instanceof checks

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!

bring your own environment

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.

Type validation using predicates rather than TypeRep's

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.

reconsider Maybe#empty

> 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?

Can't Add Additional sanctuary-def Types To Env

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

stricter S.parseFloat and S.parseInt

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?

Contracts?

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.

safe division function

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)

[Feature] Check input for pipe

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)

descriptive "method missing" errors

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)

Add S.unfoldr

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] 

add S.takeLast and S.dropLast

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. :)

match ?

Would it make sense to add a match function?

R.head(R.match(/xxx/, 'abcde')) // returns TypeError: Cannot read property '0' of null

http://bit.ly/1HasPS6

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 Maybes instead?

integer type

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.

simplify S.encase and S.encaseEither

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")

port higher-order Ramda functions which accept variadic functions

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.

ToC in readme

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?

Support ES6 Map Type

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?

Ramda String Functions

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?

toEither :: a -> b? -> Either a b

Suggested by @raine on Gitter.

> S.toEither('Invalid protocol', 'ftp://example.com/'.match(/^(http:|https:)[/][/]/))
Left("Invalid protocol")

> S.toEither('Invalid protocol', 'http://example.com/'.match(/^(http:|https:)[/][/]/))
Right(["http://", "http:"])

This would mirror S.toMaybe.

Curry-safe reduce?

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.

function equivalent of "do" notation

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 path a.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?

Add Math 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.

Even safer parseJson

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.

S.pluck?

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.

Proposal to add S.random

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.

Return Nothing() If The Object passed To Get() Is Null

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 Maybes. 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)

polymorphic functions cannot operate on "foreign" values

#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.

Type Safe Ramda Functions

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.

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?

check arguments.length in def function

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.

(pointfree) chain: Chain f => (a -> f a) -> f a -> f a

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.

Consider using Symbol instead of instanceof

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.

create stand-alone package for def function

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.

more combinators

@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. :)

Switching from ramda.pipe to sanctuary.pipe causes errors when dealing with rx

So I have a cycle.js project, I use ramda's pipe, things work fine:

screen shot 2016-03-16 at 2 21 51 am

Today I wrote a PR that switches this project to using sanctuary, but ran into some really head scratching issues:

screen shot 2016-03-16 at 2 22 09 am

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?

Ramda Hard Dependency

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.