Closed evilsoft closed 6 years ago
All errors should read something like:
3-Tuple.map: Function required
Notice the n-
in the type name
@evilsoft - I am trying to understand why we have to use the switch case to conditionally return functions. An alternate implementation follows.
function _Tuple(n, values) {
if (n !== values.length) {
throw new TypeError(
`${n}-Tuple: Expected ${n} values, but got ${values.length}`
)
}
const inspect = () => `Tuple(${values.map(_inspect).join(',')} )`
return {
constructor: _Tuple,
inspect
}
}
function Tuple(n) {
if (!isInteger(n) || n < 1) {
throw new TypeError('Tuple: Tuple size should be a number greater than 1')
}
return (...args) => _Tuple(n, args)
}
Also, this can be extended to work in a non-curried fashion (like Tuple(2, 1, 2) => Tuple(1, 2)
)
I see that you mentioned Providing the proper length for the function allows us to integrate with other libs curry and partial application helpers
. I do see that doing something like Tuple(2).length
with the above implementation would return 0. I'm trying to see use-cases where the length is important given that the type would be pre-curried.
If we do decide that accurate lengths are important for compatibility purposes, Object.defineProperty(fn, 'length', { value: n })
also does the trick while allowing arbitrary length Tuples.
Thoughts?
Well they should not be pre-curried. The types are not curried on their constructors. But if you look how ramda curries and the "short-path" for crocks curry, the length of the function comes into play: https://github.com/evilsoft/crocks/blob/master/src/core/curry.js#L17
This just allows our "short" path and will just work if a user uses ramda's curry to curry the tuple. If you look at all the types, the constructors always fully supply their inputs.
Does that make sense?
It also makes it easier (when not currying the constructor) for some IDEs to provide argument information about how the type is parameterized. If that makes sense.
I would say the lengths are important, but if you feel the defineProperty will work, give that a shot!
Ran a few quick tests, defineProperty
works with curry 🎉
Another (probably obvious) question, but Tuple(2, 1, 2) => Tuple( 1, 2 )
should not be a valid way to construct right?
Also, regarding terminology - could we consider something like mapN
or mapAll
instead of mapTuple
- It's possible that we would have similar types in the future and it'd be useful to have a generic name.
@karthikiyengar that makes sense, lets go with mapAll
.
correct about the it being invalid. We would like to let them know (at least with constructors) that there will be lost of information.
I am a little nervous about the length property, all other constructors have been explicit in their accepted arguments before. BUT I am willing to give it a shot, if you really do not want to do the switch/case thing.
@karthikiyengar after thinking about it for a bit, what would you think about nmap
or mutimap
?
@evilsoft - The idea behind getting rid of the switch case was due to the fact that as a JS lib, we're not limited by typesystem/other constraints. I've seen other language libraries (Scala Cats) limit it to 22, but mostly because they have helpers called map2
, map3
and so on.
That being said, I am completely open to doing switch/case
, if what I suggested messes with consistency. Please let me know if I am missing something - for now Tuple.length => 1
and Tuple(5).length => 5
with my current implementation.
Regarding mapAll
, sure, whatever works. As mentioned before, the Cats guys do mapN
, not sure how how/whether the Haskell guys do it.
@evilsoft - Okay, on second thought, the switch/case
looks like a better idea - I should have trusted your instinct 😄 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Compatibility_notes
@evilsoft - Pushed a change that demonstrates some weirdness in writing the argument length check (eslint-disabled for now). Thoughts?
With respect to mapAll/mapTuple/nmap/multimap
, should we have a point-free helper for this one as well? Also, we run into the same arity-length issues here, unless we switch/case
this as well.
Also, I think we missed the equals
property for this one in the description.
@evilsoft - Apart from the Tuple-10+ support, is there anything else needed to :shipit: ?
Shipped with 0.10.0. Closing
A simple Tuple type that lets us define a tuple of any size would be amazing. To get a constructor, the size of the tuple will need to be specified to get back an instance constructor, fixed to the size specified:
Implementation detail: When providing the constructor, we should return (up to Tuple-10) an anonymous function with the length of the function matching the size of the tuple. Providing the proper length for the function allows us to integrate with other libs curry and partial application helpers.
Instance Methods
concat
: when all parts are a semi-group concat them together, if not throwmap
: maps the rightmost part with provided function, throw if no function providedmapTuple
: takes one function per number of part, maps each part using the function provided for each. (basicallybimap
for tuples of n-size) Throws if the number of functions provided do not match the size of the tuple.toArray
: provides an array with n elements where n is the number of parts of the Tupleproject
: takes a positive integer n <= the size of the tuple and provides the value at that one based position, eg1 = pair.fst() = pair.toArray[0]
. throws if not an Integer1-n
where n is the total number of parts for a given Tuple. (this provides the need projections{π1, π2,...πn}
that make a Product Type a Product Type)merge
: basically exactly whatpair.merge
does.Fantasy-land
fantasy-land\concat
fantasy-land\map
Pointfree functions
mapTuple
project
merge
: This will need to be moved out ofPair
and into the general pointfree functions (Breaking change