Giter VIP home page Giter VIP logo

sanctuary-type-classes's People

Contributors

avaq avatar davidchambers avatar gabejohnson avatar kapooostin avatar masaeedu avatar paldepind avatar safareli avatar svozza avatar zhangchiqing 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

sanctuary-type-classes's Issues

Add Object$prototype$traverse

Object appears to be compatible with Traversable

// (TypeRep f, (a -> f b), StrMap a) -> f (StrMap b)
> Z.traverse(S.Maybe, S.parseInt(16), {a:'A', b:'B', c:'C'})
Just({a: 10, b: 11, c: 12})

> Z.traverse(Array, x => [x], {a: 'A', b: 'B', c: 'C'});
[{a: 'A', b: 'B', c: 'C'}]

This will allow sequence as well

// (TypeRep f, StrMap (f a)) -> f (StrMap a)
> Z.sequence(S.Maybe, {a: S.Just('A'), b: S.Just('B'), c: S.Just('C')})
Just({a: 'A', b: 'B', c: 'C'})

Z.toString cannot handle NodeJS Buffer

EDIT: I originally thought it was worse, I thought Buffer#toString throws an error when not given its encoding argument. Turns out the default behavior just returns a string of Unicode identifiers.

> Z.toString({x: new Buffer([1,2,3])})
'{"x": \u0001\u0002\u0003}'

Should we special-case this? Maybe provide an override toString function for Buffer somehow?

EDIT: I would say "no" now that I've realized that it's not throwing an error.

Add Object#ap and Object#of

In the Gitter room David demonstrated that ap can be implemented for Object. of is trivial.

// ap :: StrMap (a -> b) -> StrMap a -> StrMap b
Z.ap({}, {})
// {}
Z.ap({x: S.words, y: S.words}, {})
// {}
Z.ap({}, {x: 'foo bar', y: 'baz'})
// {}
Z.ap({x: S.words, y: S.words}, {x: 'foo bar'})
// {x: ['foo', 'bar']}
Z.ap({x: S.words, y: S.words}, {x: 'foo bar', y: 'baz'})
// {x: ['foo', 'bar'], y: ['baz']}

// of :: Object a -> StrMap a
Z.of(Object, {a: 1})
// {a : 1}
Z.of(Object, 1)
// {}

@davidchambers do I have this right?

Z.toString behaves incorrectly in Internet Explorer 9

IE9 behaviour:

> Z.toString('foo')
'new String(<Circular>)'

Expected behaviour:

> Z.toString('foo')
'"foo"'

Ecma-262 Edition 5.1 § 15.3.4.4:

The thisArg value is passed without modification as the this value. This is a change from Edition 3, where a undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value.

IE9 does not respect the 'use strict'; pragma so still has the ES3 behaviour. When evaluating String$prototype$toString.call('foo'), 'foo' is promoted to a String object (which explains the new String(…) wrapper in the return value). This would lead to an infinite promotion–demotion loop (since String$prototype$toString invokes valueOf) were it not for the circular-reference detector, which is triggered when x is 'foo' and $seen already contains 'foo' (resulting in <Circular>).

I don't know whether it's possible to fix this without giving up our elegant implementation. For now, at least, this library does not support IE9.

define Array$prototype$bimap for two-element arrays

@masaeedu shared this snippet in the Gitter room:

export const capitalizeKeys = o =>
  pipe([
    keys,
    map(k => ({ [k.toUpperCase()]: o[k] })),
    reduce(concat)({})
  ])(o);

After some discussion we agreed this code could be significantly nicer if S.pairs returned an array of honest-to-goodness pairs. One could then write:

//    capitalizeKeys :: StrMap a -> StrMap a
const capitalizeKeys = S.pipe ([S.pairs, S.map (S.mapLeft (S.toUpper)), S.fromPairs]);

This is not possible because ['foo', true] does not support bimap/mapLeft. It could do, but a problem arises when one considers map. How should S.map (S.toUpper) (['foo', 'bar']) evaluate? If one sees ['foo', 'bar'] as a member of Array String, the result should be ['FOO', 'BAR']. If, on the other hand, one sees ['foo', 'bar'] as a member of Pair String String, the result should be ['foo', 'BAR']. (Treating ['foo', true] as Pair String Boolean while treating ['foo', 'bar'] as Array String is out of the question.)

I'm reluctant to support bimap and mapLeft while leaving map out in the cold. I would rather not treat two-element arrays specially at all, and have functions such as S.pairs use the real Pair a b type soon to be released as sanctuary-pair.

@masaeedu posed an intriguing question:

Is mapRight not a thing?

It certainly could be a thing. It's trivial to derive from bimap, as was the case with mapLeft. We could provide the following four functions:

map      ::   Functor f => (a -> b) -> f a -> f b
bimap    :: Bifunctor f => (a -> b) -> (c -> d) -> f a b -> f c d
mapLeft  :: Bifunctor f => (a -> b) -> f a c -> f b c
mapRight :: Bifunctor f => (b -> c) -> f a b -> f a c

mapRight would be equivalent to map for Either a b and various other types. For Pair a b, though, or Array2 a b as it will be known if sanctuary-js/sanctuary-def#182 is merged, mapRight would behave differently from map:

> S.map (S.toUpper) (['foo', 'bar'])
['FOO', 'BAR']

> S.mapRight (S.toUpper) (['foo', 'bar'])
['foo', 'BAR']

> S.mapRight (S.not) (['foo', true])
['foo', false]

I'd still like to consider having S.pairs return a real pair rather than a two-element array, but improving the usefulness of values such as ['foo', true] may be a good idea regardless. What do you think?

Defining instances for "native" JS values (that are not Fantasy-Land compatible)

It would be nice to be able to access the same facilities Sanctuary uses to define instances for types like Array, StrMap, etc. to define instances for even more "unwrapped" values, with type membership dictated by user-specifiable predicates.

I think this might eventually boil down to the same feature as "implement static land support", but there might be other ways to accomplish this.

type of Z.of

What's the appropriate type of Z.of? There appear to be two options:

of :: Applicative f => (f a, b) -> f b
of :: Applicative f => ({ fantasy-land/of :: a -> f a }, b) -> f b

The advantage of the first option is that given Applicative f => f a we can find the of function in either of the valid locations:

typeof x['fantasy-land/of'] === 'function' ? x['fantasy-land/of']
                                           : x.constructor['fantasy-land/of']

With the second option the caller would need to do this work:

Z.of(typeof x['fantasy-land/of'] === 'function' ? x : x.constructor, 42)

We shouldn't optimize for the case in which we already have a value of the type, though. After all, the purpose of of is to allow us to create values of the type so we shouldn't need to create a value of the type in order to create a value of the type! Thus the second option seems more sensible. Do others agree?

Whatever we decide here should determine the type of Z.chainRec as well. Z.empty is a different matter because an empty method is strictly more powerful than an empty function (though this will no longer apply if and when fantasyland/fantasy-land#164 is merged).

Add support for Keyed Collections

sanctuary-type-classes should support Set, Map, and to a lesser extent, WeakSet and WeakMap.

  • empty - Set, Map, WeakSet, WeakMap
  • of - Set, Map, WeakSet, WeakMap
  • zero - Set, Map, WeakSet, WeakMap
  • equals - Set, Map
  • toString - Set, Map
  • concat - Set, Map
  • map - Set, Map
  • ap - Set, Map
  • alt - Set, Map
  • reduce - Set, Map
  • chainRec - Set
  • chain - Set
  • traverse - Set
  • extend - Set

`is` can be fooled

S.is(function Number () {}, 4)
// -> true

While documentation says:

is :: TypeRep a -⁠> Any -⁠> Boolean

Takes a type representative and a value of any
type and returns true iff the given value is of the specified type.
Subtyping is not respected.

EDIT: Sorry, wrong repo.
EDIT2: sanctuary-js/sanctuary#502.

unnecessary setoid equality check

Our equals implementations look like we call fantasy-land/equals twice, I think it unnecessary. If we try to
check equality of List or Array, we will iterate the structure twice! So, let's remove the second check.

handling of duplicate keys in Object$prototype$concat

Sanctuary:

> Z.concat({x: 1, y: 1}, {y: 2, z: 2})
{x: 1, y: 2, z: 2}

Haskell:

> M.fromList [('x', 1), ('y', 1)] <> M.fromList [('y', 2), ('z', 2)]
fromList [('x', 1), ('y', 1), ('z', 2)]

We didn't discuss this behaviour in #2 as far as I can see; I think we diverged from Haskell's behaviour unintentionally.

/cc @puffnfresh

Add `reduceMap` (aka `foldMap`)

@masaeedu was asking about implementing Haskell's foldMap :: Monoid m => (a -> m) -> t a -> m

In Haskell it's a class member of Foldable (as well as an alternate minimal definition).

I think reduceMap :: (Foldable t, Monoid m) => TypeRep m -> (a -> m) -> t a -> m would be an excellent addition to this project.

`test` methods should respect constraints

For example, Array a satisfies the requirements of Setoid if and only if a satisfies the requirements of Setoid. Z.Setoid.test currently ignores this constraint:

> var Z = require ('sanctuary-type-classes')

> var Useless = require ('sanctuary-useless')

> Z.Setoid.test (Useless)
false

> Z.Setoid.test ([Useless])
true

Z.Setoid.test ([]) and Z.Setoid.test ([1, 2, 3]) should evaluate true but Z.Setoid.test ([Useless]) should evaluate false. The behaviour of other test methods should also change.

filterM incompatible with haskell

The filterM in this library has this type

filterM :: (Alternative m, Monad m) => (a -> Boolean, m a) -> m a

whereas filterM in Haskell is (reframed slighly):

filterM :: (Monad m, Traversable f) => (a -> m Boolean, f a) -> f a

The important part is that the predicate returns inside the monad. This allows for example succinct implementation of powerset:

powerset :: [a] -> [[a]]
powerset = filterM (const [False, True])

as well as enabling logging/state/etc inside the predicate:

countingFilterM :: (a -> Bool) -> [a] -> State Int [a]
countingFilterM p = filterM (\ x -> modify succ >> return (p x))

Please consider exporting filterM to have the same functionality as in Haskell's Control.Monad.

consider ignoring inherited properties in StrMap implementations

@gabejohnson in sanctuary-js/sanctuary#435 (comment):

I don't know that we necessarily should match the semantics of S.concat as it works on objects in general. I think we may [inadvertently] cause confusion by not explicitly defining StrMap. In my opinion inherited properties should not be copied for a StrMap.

This is worth discussing. Here's an example which is not completely contrived:

function DigitCounter() {}

DigitCounter.count = ($counter, s) => {
  for (let idx = 0; idx < s.length; idx += 1) {
    const c = s[idx];
    if (/\d/.test(c)) $counter[c] += 1;
  }
};

for (let n = 0; n <= 9; n += 1) {
  DigitCounter.prototype[n] = 0;
}

const $counter = new DigitCounter();
DigitCounter.count($counter, String(Math.PI));

$counter;
// => {'1': 2, '2': 1, '3': 3, '4': 1, '5': 3, '6': 1, '7': 1, '8': 1, '9': 3}

Z.map(x => x, $counter);
// => {'0': 0, '1': 2, '2': 1, '3': 3, '4': 1, '5': 3, '6': 1, '7': 1, '8': 1, '9': 3}

Note that mapping x => x over $counter "promotes" the inherited property to an own property. The question is whether the inherited property should be ignored.

I believe the current behaviour is not just defensible but preferable. My reasoning is as follows:

  1. We currently use for ... in which respects all enumerable properties (both own and inherited). It ignores non-enumerable properties such as toString and valueOf. It's unlikely that people are affected one way or the other by our handling of enumerable inherited properties; I imagine that close to 100% of string maps are plain objects.

  2. Although obscure, the example above shows that respecting enumerable inherited properties can be useful. We should not disallow such code without good reason.

  3. Z.concat is one of the library's foundational functions. Performing a hasOwnProperty check for each key would affect the performance of several other functions as well as of Z.concat itself.

I look forward to understanding your reasons for suggesting that all enumerable inherited properties should be ignored, @gabejohnson. You may change my mind. :)

sanctuary-js/sanctuary-def#164 contains related discussion about what constitutes a record field.

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.