Kotlin泛型类型推断失败:Scala转Kotlin函数式代码求助
Hey there, let's work through this generic type inference snag you've run into while porting the State monad from Scala Functional Programming to Kotlin. I've seen this exact issue before—Kotlin's type inference works a bit differently than Scala's, especially when dealing with companion object methods that rely on class-level generics.
First, let's recap the code you shared (with the incomplete unit method filled in to match the Scala implementation):
data class State<S, out A>(val run: (S) -> Pair<A, S>) { fun <B> flatMap(f: (A) -> State<S, B>): State<S, B> = State { s -> val (a, s1) = run(s) f(a).run(s1) } fun <B> map(f: (A) -> B): State<S, B> = flatMap { a -> unit(f(a)) } companion object { fun <S, A> unit(a: A): State<S, A> = State { s -> a to s } } }
The Root of the Inference Failure
The problem happens in the map method when you call unit(f(a)). In Scala, the compiler can automatically infer that the S type parameter for unit should match the S of the current State instance. But Kotlin doesn't make that leap automatically—companion object methods don't inherit the class's generic type parameters, so the compiler can't connect the dots between the instance's S and the unit method's S.
The Fix
The simplest solution is to explicitly specify the S type parameter when calling unit in the map method. This tells Kotlin exactly which S we want to use, resolving the inference gap:
data class State<S, out A>(val run: (S) -> Pair<A, S>) { fun <B> flatMap(f: (A) -> State<S, B>): State<S, B> = State { s -> val (a, s1) = run(s) f(a).run(s1) } fun <B> map(f: (A) -> B): State<S, B> = flatMap { a -> // Explicitly pass the instance's S type to unit State.unit<S, B>(f(a)) } companion object { fun <S, A> unit(a: A): State<S, A> = State { s -> a to s } } }
Why This Works
By adding <S, B> to the unit call, we're explicitly tying the companion method's generic parameters to the current State instance's S type. Kotlin can now correctly infer that the State returned by unit should use the same state type S as the original instance, which aligns with the map method's return type requirement of State<S, B>.
Alternative Approach (For Cleaner Syntax)
If you prefer to avoid explicit type parameters, you can define unit as an extension method on the State class instead of putting it in the companion object. This lets the method inherit the class's S parameter automatically:
data class State<S, out A>(val run: (S) -> Pair<A, S>) { fun <B> flatMap(f: (A) -> State<S, B>): State<S, B> = State { s -> val (a, s1) = run(s) f(a).run(s1) } fun <B> map(f: (A) -> B): State<S, B> = flatMap { a -> unit(f(a)) } } // Extension method instead of companion object fun <S, A> State<S, *>.unit(a: A): State<S, A> = State { s -> a to s }
This way, when you call unit inside map, Kotlin already knows the S type from the instance context, so no explicit parameters are needed.
内容的提问来源于stack exchange,提问作者David

