栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么不能用Java声明Monad接口?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么不能用Java声明Monad接口?

Java类型系统缺少什么功能?这些其他语言如何声明Monad类型?

好问题!

埃里克·利珀特(Eric Lippert)将其称为高级类型,但我无法直视它们。

你不是一个人。但是他们实际上并不像听起来那样疯狂。

让我们通过查看Haskell如何声明monad为“类型”来回答您的两个问题-
您将在一分钟内看到为什么引用。我做了一些简化。标准monad模式在Haskell中还有其他几个操作:

class Monad m where  (>>=) :: m a -> (a -> m b) -> m b  return :: a -> m a

男孩,看起来既简单又完全不透明,不是吗?

在这里,让我简化一下。Haskell让您声明自己的infix运算符进行绑定,但是我们将其称为bind:

class Monad m where  bind :: m a -> (a -> m b) -> m b  return :: a -> m a

好吧,至少现在我们可以看到其中有两个monad操作。其余的是什么意思?

如您所知,首先要注意的是“更高种类的类型”。(正如Brian指出的那样,我在最初的回答中稍微简化了这个行话。您的问题吸引了Brian的注意也很有趣!)

在Java中,“类”是 一种
“类型”,并且类可以是通用的。因此,在Java中,我们已经有了

int
,并
IFrob
List<IBar>
他们是所有类型。

从这时起,您就不再有关于长颈鹿是动物的子类的类的直觉了,等等。我们不需要。想想一个没有继承的世界;它不会再进入此讨论。

Java中的类是什么?好吧,最简单的方式来考虑一个类是,它是一 组具有共同点的值名称, 以便
需要该类的实例时可以使用这些值中的任何一个。您有一个类

Point
,可以说,如果您有一个type变量
Point
,则可以为其分配任何实例
Point
。从
Point
某种意义上讲,类只是描述
所有
Point
实例的集合的_一种方式。类比 _实例高

在Haskell中,还有通用类型和非通用类型。Haskell中的类 不是 一种类型。在Java中,类描述了一组
;任何时候需要该类的实例时,都可以使用该类型的值。在Haskell中,一个类描述了一组 类型 。这是Java类型系统缺少的关键功能。在Haskell中
,类高于类型,而类型高于实例。
Java只有两个层次结构;Haskell有三个。在Haskell中,您可以表达这样的想法:“只要我需要具有某些操作的类型,就可以使用该类的成员”。

(旁白:我想指出的是,我有点过分简化了。例如

List<int>
,以Java
和Java为例
List<String>
。这是两个“类型”,但Java认为它们是一个“类”,因此从某种意义上讲Java也它的类比类型“高”,但是再说一遍,您可以在Haskell中说
listx
list y
类型,
list
这是比类型高的东西;它是可以产生类型的东西。实际上,说Java有 3个 级别,而Haskell有 4
级别,则更准确地说,不过,重点仍然是:Haskell的概念是描述比Java更强大的类型上可用的操作。这在下面有更详细的说明。)

那么这与接口有何不同?这听起来像Java中的接口-您需要一种具有某些操作的类型,然后定义一个描述这些操作的接口。我们将看到Java接口缺少的内容。

现在我们可以开始理解这个Haskell了:

class Monad m where

那么,什么是

Monad
?这是一堂课。什么是课程?它是一组具有一些共同点的类型,因此只要您需要具有某些操作的类型,就可以使用
Monad
类型。

假设我们有一个属于该类的类型;称呼它

m
。为了使该类型成为类的成员,必须对该类型进行哪些操作
Monad

  bind :: m a -> (a -> m b) -> m b  return :: a -> m a

操作的名称位于的左侧,

::
签名位于右侧。因此
Monad
,类型
m
必须具有两个操作:
bind
return
。这些操作的签名是什么?让我们
return
先来看。

  a -> m a

m a
是Haskell的Java语言
M<A>
。也就是说,这种手段
m
是一个通用型,
a
是一种类型,
m a
m
参数化用
a

x -> y
Haskell中的语法是“具有类型
x
并返回类型的函数
y
”。是
Function<X, Y>

放在一起,我们就有

return
了一个函数,该函数接受type的参数
a
并返回type 的值
m a
。或用Java

static <A>  M<A> Return(A a);

bind
有点难。我认为OP非常了解此签名,但是对于不熟悉简洁的Haskell语法的读者,让我对其进行扩展。

在Haskell中,函数仅接受一个参数。如果要使用两个参数的函数,则可以创建一个接受一个参数并返回 另一个具有一个参数的函数的函数 。所以如果你有

a -> b -> c

那你有什么 接受

a
并返回的函数
b->c
。因此,假设您想创建一个接受两个数字并返回其总和的函数。您将创建一个使用第一个数字的函数,并返回一个使用第二个数字并将其添加到第一个数字的函数。

在Java中,您会说

static <A, B, C>  Function<B, C> F(A a)

因此,如果您想要C,并且拥有A和B,则可以说

F(a)(b)

合理?

好吧

  bind :: m a -> (a -> m b) -> m b

实际上是一个需要两件事的函数:an

m a
和a
a -> m b
并返回an
m b
。或者,在Java中,它直接是:

static <A, B> Function<Function<A, M<B>>, M<B>> Bind(M<A>)

或者,更惯用Java:

static <A, B> M<B> Bind(M<A>, Function<A, M<B>>)

现在,您了解了Java为什么不能直接表示monad类型的原因。它没有能力说“我有一类具有相同模式的类型”。

现在,您可以在Java中创建所需的所有monadic类型。您不能做的是创建一个表示“此类型为monad类型”想法的接口。您需要做的是:

typeinterface Monad<M>{  static <A>    M<A> Return(A a);  static <A, B> M<B> Bind(M<A> m, Function<A, M<B>> f);}

看到类型接口如何谈论泛型类型本身吗?一元类型是

M
具有一个类型参数 具有这两种 静态
方法的任何类型。但是您不能在Java或C#类型的系统中执行此操作。
Bind
当然可以是采用
M<A>
as
的实例方法
this
。但是
Return
除了静态之外,别无他法。Java无法让您(1)通过未 构造的
泛型类型对接口进行参数化,并且(2)无法指定静态成员是接口协定的一部分。

由于存在适用于monad的语言,因此这些语言必须以某种方式声明Monad类型。

好吧,您会这样想,但实际上却没有。首先,当然,任何具有足够类型系统的语言都可以定义单子类型。您可以在C#或Java中定义所需的所有monadic类型,只是不能说出它们在类型系统中的共同点。例如,您不能创建只能通过monadic类型进行参数化的泛型类。

其次,您可以通过其他方式在语言中嵌入monad模式。C#无法说“此类型与monad模式匹配”,但是C#具有内置于该语言中的查询理解(LINQ)。查询理解适用于任何单子类型!只是必须调用bind操作

SelectMany
,这有点奇怪。但是,如果您看一下的签名
SelectMany
,就会发现它只是
bind

  static IEnumerable<R> SelectMany<S, R>(    IEnumerable<S> source,    Func<S, IEnumerable<R>> selector)

这是

SelectMany
序列monad 的实现
IEnumerable<T>
,但如果您编写的是C#

from x in a from y in b select z

然后

a
的类型可以是 任何
单子类型,而不仅仅是
IEnumerable<T>
。所需要的是该
a
M<A>
,即
b
M<B>
,并且有合适的
SelectMany
随后的单子图案。因此,这是在语言中嵌入“ monad识别器”而不直接在类型系统中表示的另一种方法。

(上一段实际上是一个过分简化的谎言;出于性能原因,此查询使用的绑定模式与标准monadic绑定略有不同。 从概念上讲,
这可以识别monad模式;实际上,细节略有不同。请在此处阅读有关详细信息:http:
//ericlippert.com/2013/04/02/monads-part-
twelve/(如果您有兴趣)。

还有几点要点:

我找不到第三项操作的常用名称,因此我将其称为unbox函数。

好的选择; 它通常称为“提取”操作。一个 monad
不必公开提取操作,但是当然

bind
需要某种方式才能
A
退出
M<A>
调用
Function<A, M<B>>
它,因此在逻辑上通常存在某种提取操作。

一个 共鸣 -从某种意义上说是向后的单子-
要求

extract
公开操作;
extract
本质上是
return
倒退的。同样,comonad也需要进行
extend
某种
bind
后退的操作。有签名
staticM<B> Extend(M<A> m, Func<M<A>, B> f)



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/508087.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号