0
interface Monoid<T>{
    operator fun plus(other:T):T
    fun zero():T
}
class MyList<T:Monoid<T>>(val l:List<T>){
    fun sum():T{
        var result:T = ... // what should be here? Ideally it could be smth like T.Zero or T.zero() with static
        for (el in l){
            result += el
        }
        return result
    }
}

I have this case. The idea is to build list class that can get usual list (which elements are of Monoid type) in constructor and after finish of build be ready to sum all of it's elements. The problem is when list is empty.

Initially i thought that this case is not problem for java (because i thought it has more flexible static attribute), but after some search, when i understood that static can't be abstract and that interface static method should be predefined, my opinion changed.

I'm little bit new in kotlin and my experience in java isn't big enough.

But I've got that if T is reified then i can access it's method via T::class.java.getMethod(name). But to apply it, i still need instance of T. Also I've found that i can access the constructor via T::class.java.getConstructor(params). But even if it is too perfectionist, I thought: what if i don't know what kind (and count) of arguments can T constructor get? What if it doesn't have default constructor with zero arguments? So the restriction to solve my problem is that T must be able to have any type of constructors.

The problem could be simply solved if I could access to companion object of T or if i could choose either to inherit from companion object of interface or not. But i can't because it exists independently. Companion object even does not have access to generic type, so i can't feed it with "fun zero():T"

So my question is how can i manage this problem.

Of course i can make life simpler for example by defining zero element especially for each MyList<T> instance (but in this case T does not need to be monoid) or by creating another interface for T that obligate it to have zero-arguments constructor. But these ways (both) still look not clean.

interface Monoid<T>{
    operator fun plus(other:T):T
    fun zero():T
}
interface NeutralForMonoid<T>{
    fun zero():T
}
class MyList<T:Monoid<T>>(val l:List<T>){
    fun sum():T{
            var result:T = ... 
// what should be here? Ideally it could be smth like T.Zero or T.zero() with static
        for (el in l){
            result += el
        }
        return result
    }
    companion object : NeutralForMonoid<T>{
            ...
    }
}

^^^^^^^^^^^^^^^^^^^

even this way looks not so bad for me, but it still is not possible because companion object can't know anything about it's surrounding class.

If the way I've chosen to solve this problem isn't best I'll be thankful for another ways.

1 Answers1

0

Taking ideas from this article: https://eweise.com/post/category-into/

I think the significant step is to define the sum method via an Extension Method on the Monoid, thereby allowing you access this.zero().

I also changed the Monoid not to use the operator...plus because I couldn't resolve a different problem, but perhaps this is useful to you anyway in your quest.

    interface Monoid<T>{
        fun combine(t1:T, t2:T):T
        fun zero():T
    }
    fun <T> Monoid<T>.sum(ts:List<T>):T {
        return ts.foldRight(this.zero()) { t: T, accumulator: T -> combine(t, accumulator) }
    }
AndrewL
  • 2,034
  • 18
  • 18
  • well, it is not close enough to decision, because I still need implementation of Monoid to call sum method while i don't have it in case of empty list. The problem is that information about what is zero and plus (or combine) is stored in type T (T:Monoid), and i can't access it without instance of T. – Paul Ivanov Jun 29 '23 at 18:32