sanctuary-js / sanctuary

:see_no_evil: Refuge from unsafe JavaScript
https://sanctuary.js.org
MIT License
3.03k stars 94 forks source link

Z.concat doesn't work with any Semigroup, only with sanctuary defined #643

Closed paranormal closed 5 years ago

paranormal commented 5 years ago

From how it works I think it's not very interoperable. Let's say I have hand-made Semogroup

const Sum = x => ({
  x,
  concat: ({ x: y }) => Sum(x + y),
  [Symbol.for('nodejs.util.inspect.custom')]: () => `Sum(${x})`,
})

It obeys the laws, nothing is wrong with it, it misses input type checks but this is a different story... https://github.com/sanctuary-js/sanctuary-type-classes/blob/v11.0.0/index.js#L1393

Why this function doesn't check it's input parameters for .concat method and use it instead?

davidchambers commented 5 years ago

The method actually needs to be named fantasy-land/concat:

//    Sum :: Number -> Sum
const Sum = value => ({
  value,
  [util.inspect.custom]: () => `Sum (${value})`,
  'fantasy-land/concat': sum => Sum (value + sum.value),
});

console.log (Z.concat (Sum (14), Sum (28)));  // -> Sum (42)
paranormal commented 5 years ago

Thank you very much, I was playing with Pair as an Apply and hit this. Is there any reason why it should be fantasy-land/concat? It's different from how strings or arrays works.

davidchambers commented 4 years ago

Is there any reason why it should be fantasy-land/concat?

The prefix makes it possible to add a compliant fantasy-land/concat method to a type that already has a non-compliant concat method. The prefix also makes it obvious whether a particular method is intended to comply with the Fantasy Land specification.

It's different from how strings or arrays works.

True. It's worth noting that String#concat and Array#concat are specialized in ways that their Fantasy Land counterparts are not:

> 'foo'.concat('bar', 'baz')
'foobarbaz'

> ['foo'].concat('bar')
['foo', 'bar']

> ['foo'].concat(['bar'], ['baz'])
['foo', 'bar', 'baz']

I recommend using S.concat as it provides a consistent interface:

> S.concat ('foo') ('bar')
'foobar'

> S.concat (['foo']) (['bar', 'baz'])
['foo', 'bar', 'baz']

> S.concat (S.Just ('foo')) (S.Just ('bar'))
Just ('foobar')
paranormal commented 4 years ago

Thank you for this very descriptive explanation.