Scala Can';t在monad的自写实例上使用flatMap作为扩展方法
我曾尝试在WriterT上使用flatMap,并取得了成功 所以问题可能出在我的类型上,但我找不到它有什么问题Scala Can';t在monad的自写实例上使用flatMap作为扩展方法,scala,scala-cats,dotty,scala-3,Scala,Scala Cats,Dotty,Scala 3,我曾尝试在WriterT上使用flatMap,并取得了成功 所以问题可能出在我的类型上,但我找不到它有什么问题 import cats.Monad 导入cats.syntax.flatMap_ 对象主应用程序{ 类型可选[A]=A |空 val maybeInt1:可选[Int]=1 val maybeInt2:可选[Int]=null 给定Monad[可选]和{ def纯[A](x:A):可选[A]=x def平面地图[A,B](fa:可选[A])(f:A=>可选[B]):可选[B]={ 足总
import cats.Monad
导入cats.syntax.flatMap_
对象主应用程序{
类型可选[A]=A |空
val maybeInt1:可选[Int]=1
val maybeInt2:可选[Int]=null
给定Monad[可选]和{
def纯[A](x:A):可选[A]=x
def平面地图[A,B](fa:可选[A])(f:A=>可选[B]):可选[B]={
足总杯比赛{
大小写null=>null
案例a:a=>f(a)
}
}
def tailRecM[A,B](A:A)(f:A=>可选[A,B]]:可选[B]={
f(a)比赛{
大小写null=>null
案例左侧(a1)=>tailRecM(a1)(f)
案例右侧(b)=>b
}
}
}
def[f[uz]:Monad,A,B](A:f[A],B:f[B])=A.flatMap(z=>B)
println(Monad[Optional].flatMap(maybeInt1)(=>maybeInt2))//确定:null
println(f[Optional,Int,Int](maybeInt1,maybeInt2))//确定:null
println(maybeInt1.flatMap(=>maybeInt2))//编译错误
}
错误是:
值flatMap不是Main.Optional[Int]的成员尝试了扩展方法,但无法完全构造:
cats.syntax.flatMap.toFlatMapOps([A]=>>Any),A(给定的单子是可选的)
你的定义有几个问题 问题1。您正在对非参数化类型使用非不透明类型别名 也就是说,
type Optional[A]=A | Null
是一个将尽快展开的类型表达式。
当您使用它作为结果类型时,您实际得到的是
val-maybeInt1:Int | Null=1
val maybeInt2:Int | Null=Null
因此,当编译编译器具有类似
implicit-def-toFlatMapOps[F[_],A](fa:F[A])(implicit-F:Monad[F]):MonadOps[F,A]
从scala 2库或scala 3中的等效扩展导入,
最后是maybepoption.flatMap
,然后尝试应用前面的扩展方法,
它无法对表达式进行打字检查
toFlatMapOps(maybeInt1)。flatMap(=>maybeInt2)
因此,现在您有了Int | Null
作为参数,因为Optional
已经展开,需要计算相应的F[\u]
和A
,它有许多解决方案,例如
F[X]=Int | X,A=Null
F[X]=X |空,A=Int
F[X]=A | Null,A=Nothing
F[X]=[X]=>>X,A=Int | Null
Monad
的隐式值是
给定单子[可选]
现在可以尝试申请了
toFlatMapOps[F=Maybe](maybeInt1:Int | Null)
然后让F[X]=X | Null
你需要计算A
知道F[A]=Null | A
,这也有很多合理的解决方案
A=Int
A=Int|Null
scalacOptions+=“-Yexplicit nulls”
添加到您的sbt配置中,然后尝试此代码
import cats.Monad
导入cats.syntax.flatMap.given
对象可选:
不透明类型可选[+A]>:A | Null=A | Null
扩展名[A](oa:可选[A])定义值:A | Null=oa
给定Monad[可选]和
def纯[A](x:A):可选[A]=x
def flatMap[A,B](fa:A | Null)(f:A=>B | Null)=
如果fa==null,则为null,否则为f(fa)
def tailRecM[A,B](A:A)(f:A=>可选[A,B]]:可选[B]=
f(a)比赛
大小写null=>null
案例左侧(a1)=>tailRecM(a1)(f)
案例右侧(b)=>b
输入可选[+A]=可选。可选[A]
@主def运行=
val maybeInt1:可选[Int]=1
val maybeInt2:可选[Int]=null
def[f[uz]:Monad,A,B](A:f[A],B:f[B])=A.flatMap(z=>B)
println(Monad[Optional].flatMap(maybeInt1)(=>maybeInt2))//确定:null
println(f(maybeInt1,maybeInt2))//确定:null
println(maybeInt1.flatMap(=>maybeInt2))//编译错误
问题2。这种类型不是单子
即使在这个固定版本中,可选[A]
也违反了基本的一元法则
考虑这个代码
def-orElse[F[_],A](fa:F[Optional[A]])(默认值:=>F[A])(使用F:Monad[F]):F[A]=
fa.map(u.value).flatMap(fb=>如果fb==null,则默认为else F.pure(fb:A))
def filterOne(x:Int):可选[Int]=如果x==1,则为null,否则为x-1
println(orElse(maybeInt1.map(filteron))(3))
第一种方法尝试用给定的计算出的一元值来解决缺少的值,第二种方法只是过滤掉那些值。
那么,当这样的东西被评估时,我们期望看到什么呢
orElse(maybeInt1.map(filterOne))(3)
我们可能会选择非空,然后用缺少的位置替换1
,然后立即使用提供的3
修复它。因此,我希望看到3
,但实际上,我们得到了null
,这是因为在flatMap期间,null
在包装值内部被认为是外部可选
的缺失分支。
这是因为这种简单定义的类型违反了
更新
关于
这个定义是如何违反左身份法的。
左派身份声明
pure(a).flatMap(f) == f(a)
for all types A, B, and values a: A, f: A => Optional[B]
让我们取A=Optional[Int],B=Int,A=null,f(A)=如果A=null,那么取3,否则取2
pure(a)仍然为null,flatMap为第一个参数中的每个参数返回null,所以
pure(a).flatMap(f)==null
而f(a)==3