Closed KristianBalaj closed 2 years ago
Hi @KristianBalaj!
Interesting proposal! Could you give an example of what kind of code you would want to be able to write? I'm not sure I understand what you are looking for... Using the methods on Monoid
as instance methods on custom types would probably be a bit awkward:
// odd to have to instantiate an A just to be able to get to empty()
final anEmptyA = (A()).empty();
// creates a new value based on secondA and thirdA, but the value of firstA is irrelevant and ignored?
final fourthA = firstA.append(secondA, thirdA);
The API for Monoid
is designed for "standalone" use ("type X forms a monoid" rather than "type X is a monoid") and also allows for types to form monoids (and semigroups) in several ways, like in:
final nums = ilist<num>([1,2,3,4]);
print(nums.concatenate(NumSumMi)); // => 10
print(nums.concatenate(NumProductMi)); // => 24
print(nums.concatenateO(NumMaxSi)); // => Some(4)
Several type classes in dartz
have accompanying Ops
types (Foldable
/FoldableOps
, Functor
/FunctorOps
, etc) that are intended to provide "type X is a ..." semantics to other types. These provide OO-friendly versions of the "standalone" methods provided by their respective type classes. Most of them are sadly not usable as mixins, since Dart mixins are currently restricted to inheriting from Object
only. "Being" MonadOps
implies also "being" ApplicativeOps
and FunctorOps
. Similarly, "being" MonoidOps
would naturally imply also "being" SemigroupOps
.
No MonoidOps
currently exists and I'm not sure how useful it would be in practice, but maybe that's what you are looking for?
Well a simple example would be having a class BankAccount
that has fields like balance, transactions, etc.
Inheriting BankAccount
from any other class makes sense, e.g. Equatable
.
Making the BankAccount
a monoid makes sense here, where the append
has semantics of BankAccount
merge for example.
And let's take an example where I want to compose a general function taking List
s of Monoid
s and the result of this function would be a Monoid
.
This is one of the many possible examples why making a Monoid
by inheritance is not a good solution in my opinion.
But since you said that mixins can't be extended what I didn't know initially, then this issue doesn't probably have a proper solution in Dart language.
Also, other problem is that you cannot call the Monoid.empty
method without an instance of the object what makes it impossible to compose such a general function as mentioned in the Example (in case the parameter of the function is an empty List
. In that case, you have just the generic parameter T extends Monoid<T>
and an empty List<T>
but you can't return the empty
since u don't have an instance of T
. Maybe it would work with some rape using reflection - instantiating object from type and calling empty on it, but it is nasty).
Let's have a class
A
that is aMonoid
:Problem
Imagine for example that class
A
is a huge data class and I want to subclass it withEquatable
to solve the equality logic for me. After that I can't make the classA
to be aMonoid
. I could makeclass A extends Equatable implements Monoid<A>
but after that I need to override 3 methods, theappendC
that shouldn't be necessary to override forMonoid
.Making a
Monoid
a mixin would solve this problem as followingclass A extends Equatable with Monoid<A>
and then there are the 2Monoid
methods to override as expected.This should be done for all the other typeclasses as well.
Notice
However, this would be a breaking change, breaking all the existing code, where developers would need to replace
extends
with thewith
keyword.