Scala 接受两个一元值并返回单个一元值的泛型函数

Scala 接受两个一元值并返回单个一元值的泛型函数,scala,haskell,generics,monads,Scala,Haskell,Generics,Monads,我已经编写了以下Haskell函数,它接受两个一元值并将它们组合成一个一元值(这只是为了说明Haskell类型系统可以支持的泛型(或泛型)程度) combine x y=do 您的combine函数相当于Scala代码 for { a <- x; b <- y } yield (a,b) (我正在为使用名称,并使用与Monad稍有不同的界面) 然后,我们为我们想要支持的每一种数据类型提供这个特性的隐式实现。下面是选项的一个示例: implicit val optionFor = n

我已经编写了以下Haskell函数,它接受两个一元值并将它们组合成一个一元值(这只是为了说明Haskell类型系统可以支持的泛型(或泛型)程度)

combine x y=do

您的
combine
函数相当于Scala代码

for { a <- x; b <- y } yield (a,b)
(我正在为
使用名称
,并使用与
Monad
稍有不同的界面)

然后,我们为我们想要支持的每一种数据类型提供这个特性的隐式实现。下面是
选项的一个示例

implicit val optionFor = new For[Option] {
  def map[A,B](ma: Option[A], f: A => B): Option[B] = ma.map(f)
  def flatMap[A,B](ma: Option[A],f: A => Option[B]): Option[B] = ma.flatMap(f)
  def withFilter[A](ma: Option[A],q: A => Boolean): Option[A] = ma.withFilter(q).map(a => a)
}
然后,我们提供对可以应用这些操作的类型的隐式转换:

implicit class ForOps[M[_], A](val ma: M[A]) extends AnyVal {
  def map[B](f: A => B)(implicit m: For[M]): M[B] = m.map(ma,f)
  def flatMap[B](f: A => M[B])(implicit m: For[M]): M[B] = m.flatMap(ma, f)
  def withFilter(q: A => Boolean)(implicit m: For[M]): M[A] = m.withFilter(ma,q)
}
最后,我们可以定义
组合

def combine[M[_]: For, A, B](ma: M[A], mb: M[B]): M[(A, B)] =
  for { a <- ma; b <- mb } yield (a, b)
糖是用来做什么的

def f[T](implicit unutterableName: TC[T]) = ???
如果调用站点没有明确给出
隐式
参数列表,则将通过搜索具有正确类型的值/函数自动填充,只要这些值/函数本身是
隐式
。在本例中,我们寻找
M
是单子的证明。在主体中,该值是隐式的,没有名称可以访问它。隐式搜索仍然可以找到它
ForOps
通过使用该
Monad
允许3
for
操作自动显示在值上


这实际上是GHC如何实现类型类的显式版本。在没有优化的最简单情况下:

class Applicative m=>Monad m其中
return::a->ma
(>>=)::MA->(a->MB)->MB
编译成

data Monad m=Monad{
monadSubApplicative::Applicative m
return::for all a.a->m a
(>>=)::对于所有a.m.a->(a->m.b)->m.b
}

实例Monad[]其中
返回=_
(>>=) = _
变成

monadList::Monad[]
单子列表=单子{
monadSubApplicative=应用程序列表
,返回=_
, (>>=) = _
}
您经常会听到“dictionary”一词用于描述底层数据类型和值。而
组合

combine::Monad m->ma->mb->m(a,b)
合并(单子返回(>>=)ma mb=ma>=\a->mb>>=\b->返回(a,b)
然而,GHC对系统应用了一系列限制,使其更具可预测性并执行更多优化。Scala牺牲了这一点,允许程序员执行更有趣的杂技

例如:

newtype Compose f g a=Compose{unCompose::f(g a)}
实例(函子f,函子g)=>函子(组合f g),其中
fmap f(合成fga)=合成$fmap(fmap f)fga
在Scala中这样做(使用
隐式def
,而不是
val
):

trait函子[F[]]{def映射[A,B](fa:F[A])(F:A=>B):F[B]}
最后一个case类Compose[F[u],G[u],A](val get:F[G[A]])扩展了AnyVal
对象组合{
//您通常将这些隐含信息放在关联的伙伴中
//因为隐式搜索对它的位置很挑剔
隐式def函子[F[_],G[_]](隐式
函子F:函子[F],
函子:函子[G]
//类型lambda:在细化类型上使用类型投影
//创建匿名类型级别函数的步骤
//这被普遍认为是对语法的可怕滥用
//您可以使用kind projector插件来避免编写它们(直接)
):函子[({type L[X]=Compose[F,G,X]})#L]
=新函子[({type L[X]=Compose[F,G,X]})#L]{
覆盖def映射[A,B](cfga:Compose[F,G,A])(F:A=>B):Compose[F,G,B]=
Compose(functorF.map(cfga.get){ga=>functorG.map(ga)(f)})
}
}

将所有这些内容明确化有点难看,但效果相当不错。

我认为这是等效的:

import cats._
import cats.implicits._

def combine[T, F[_]: Monad](fa: F[T], fb: F[T]) = for {
    a <- fa
    b <- fb
  } yield (a, b)
导入猫_
进口猫_
def combine[T,F[(单子)(fa:F[T],fb:F[T])=for{
a在函数式编程(以及一般编程)中,最好使用您能找到的功能最低的抽象。在您给出的示例中,您实际上不需要monad的功能。combine函数是applicative type类中的liftA2。示例:

import Data.Maybe
import Control.Applicative
z= Just 1
y= Just 2
liftA2 (,) z y
> Just (1,2)
Scala中也有类似的东西。Scalaz库中的一个例子使用了相同的抽象:

import scalaz._, Scalaz._
(Option(1) |@| Option(2))(Tuple2.apply)
> res3: Option[(Int, Int)] = Some((1, 2))

你不需要单子抽象的原因是这些值彼此独立。

?@max630:这有什么帮助吗?如果有,你能给出一个答案吗?@Nawaz,当我们把
f
看作一个应用函子(所有单子都是应用函子)时,调用
f a类型的值是很常见的“操作”,不管它们是否真的“做了些什么”。您的
combine
函数可用于所有应用函数:
combine::applicative f=>fa->fb->f(a,b);combine=liftA2(,)
。事实上,即使是
Applicative
也比您需要的稍多。您可以使用
combine::Apply f=>fa->fb->f(a,b);combine xs ys=(,)xs ys
。但是
Apply
不是
Applicative
的超类,所以在实践中这可能不明智。@d您能否提供
Apply
的链接?我能找到的最接近的东西是PureScript的
Apply
类,但它没有定义
@chepner,它在
半群体中将其设置为
Applicative
的超类,因此它不需要命名奇怪的运算符。我忘记了
liftF2
,这显然是在这里使用的更好的方法。”编译器会抱怨flatMap不是M[a]的成员,map不是M[B]的成员。“…那么,您所编写的不会实际编译和运行吗?第一行很好(仅用于理解本身的
)。此处所写的函数
组合
的定义将不起作用。如果您将其与imple进行比较
import cats._
import cats.implicits._

def combine[T, F[_]: Monad](fa: F[T], fb: F[T]) = for {
    a <- fa
    b <- fb
  } yield (a, b)
import Data.Maybe
import Control.Applicative
z= Just 1
y= Just 2
liftA2 (,) z y
> Just (1,2)
import scalaz._, Scalaz._
(Option(1) |@| Option(2))(Tuple2.apply)
> res3: Option[(Int, Int)] = Some((1, 2))