-
Most type class instances in fp-ts can be created by implementing an interface of a specific type arity for one's type URI (given that the const URI = 'Array';
type URI = typeof URI;
// Functor instance for "Array" types of arity 1
const ArrayFunctor: Functor1<URI> = {URI, map: (fa, f) => fa.map(f)}; But Semigroup (and Monoid, possibly others) seem to be an exception. They're just interfaces that take a concrete type: const ArrayNumberSemigroup: Semigroup<Array<number>> = {concat: (a, b) => a.concat(b)} To avoid repetition, the pattern I see in fp-ts is "factory" functions for semigroups. Typically, these won't take any arguments, and the only reason they're a function is to pass the type parameter via a generic: const getArraySemigroup: <T>() => Semigroup<Array<T>> = {concat: (a, b) => a.concat(b)} But couldn't we avoid repetition in the same way it's done for most other type classes? export interface Semigroup1<F extends URIS> {
readonly URI: F
readonly concat: <T>(a: Kind<F, T>, b: Kind<F, T>) => Kind<F, T>
}
const ArraySemigroup: Semigroup1<URI> = {URI, concat: (a, b) => a.concat(b)} From what I can tell with some shallow testing, this works just fine. What's the reason that Semigroup and Monoid don't use this approach? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
I realized what the answer to this is. 🤦 It has to do with the kind of the types that the class has been defined for. Functor must take a higher kinded type of arity 1, whereas Semigroup can also be implemented for basic types (of arity 0). |
Beta Was this translation helpful? Give feedback.
I realized what the answer to this is. 🤦
It has to do with the kind of the types that the class has been defined for. Functor must take a higher kinded type of arity 1, whereas Semigroup can also be implemented for basic types (of arity 0).