如何在Language Ext中用WriterT堆叠Writer与带Runtime的Eff monad?
在LanguageExt中结合WriterT与Eff
问题背景
尝试通过WriterT转换器同时使用带Runtime参数的Eff<RT> monad和Writer功能,但代码无法编译,原代码如下:
public static Eff<RT, bool> doThingWithRuntime<RT>() where RT: struct => liftEff(rt => true); public static WriterT<StringM, Eff<RT>, string> SayHelloWorld<RT>() where RT: struct => from _1 in tell<StringM>("world") from _2 in doThingWithRuntime<RT>() // 此行存在问题 from _3 in tell<StringM>("!") select "hello";
编译器报错:
Error CS0266 : 无法将类型“LanguageExt.WriterT<LanguageExt.StringM, LanguageExt.Eff<RT>, bool>”隐式转换为“LanguageExt.Traits.K<LanguageExt.WriterT<LanguageExt.StringM, LanguageExt.Eff>, bool>”。存在显式转换(是否缺少强制转换?) Error CS0029 : 无法将类型“LanguageExt.WriterT<LanguageExt.StringM, LanguageExt.Eff<RT>, bool>”隐式转换为“LanguageExt.WriterT<LanguageExt.StringM, LanguageExt.Eff, bool>”
替换为无参数的Eff时代码正常,说明问题出在Eff<RT>的类型适配上。
错误原因
核心问题是LINQ查询中各步骤的WriterT底层monad类型不统一:
tell<StringM>("world")默认返回WriterT<StringM, Eff, Unit>,底层monad是无Runtime参数的EffdoThingWithRuntime<RT>()是Eff<RT, bool>,即使通过WriterT.lift提升后,得到的是WriterT<StringM, Eff<RT>, bool>,底层monad是带RT参数的Eff<RT>
两种WriterT的底层monad类型不兼容,编译器无法完成隐式转换,因此报错。
解决方案
需要确保所有WriterT操作的底层monad统一为Eff<RT>,具体步骤如下:
- 调用
tell时明确指定底层monad为Eff<RT>,使用WriterT.tell<StringM, Eff<RT>>()替代默认的tell<StringM>() - 使用
WriterT.lift将Eff<RT, bool>正确提升为同底层monad的WriterT类型
修正后的代码:
using LanguageExt; using static LanguageExt.Prelude; public static Eff<RT, bool> doThingWithRuntime<RT>() where RT : struct => liftEff((RT rt) => true); public static WriterT<StringM, Eff<RT>, string> SayHelloWorld<RT>() where RT : struct => from _1 in WriterT.tell<StringM, Eff<RT>>("world") from _2 in WriterT.lift<StringM, Eff<RT>, bool>(doThingWithRuntime<RT>()) from _3 in WriterT.tell<StringM, Eff<RT>>("!") select "hello";
这样所有步骤的WriterT都基于Eff<RT>作为底层monad,类型完全匹配,编译器可以正常处理LINQ查询的拼接。
内容的提问来源于stack exchange,提问作者Andy Charalambous
相关产品推荐
相关产品推荐

