Scala Can';t在monad的自写实例上使用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]={ 足总

我曾尝试在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]={
足总杯比赛{
大小写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
  • 因此scala自然无法进行这种猜测

    尽管scala 3编译器可以在此处使用其他信息,例如隐式\上下文值,但在此处匹配优先级最高的
    Monad
    的隐式值是

    给定单子[可选]
    
    现在可以尝试申请了 toFlatMapOps[F=Maybe](maybeInt1:Int | Null) 然后让
    F[X]=X | Null
    你需要计算
    A
    知道
    F[A]=Null | A
    ,这也有很多合理的解决方案

  • A=Int
  • A=Int|Null
  • 因此,即使scala在第一步没有失败,它也会停留在这里

    解决方案1。使用不透明类型别名 将
    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