Scala 和 Frege 都是针对 JVM 的类型化函数式语言。
Frege 更接近 Haskell,Scala 有更独立的历史。
但是如果我们不看句法上的差异,那么两者在允许的编程技术、风格、概念上有什么不同呢?
Scala 和 Frege 都是针对 JVM 的类型化函数式语言。
Frege 更接近 Haskell,Scala 有更独立的历史。
但是如果我们不看句法上的差异,那么两者在允许的编程技术、风格、概念上有什么不同呢?
恕我直言,两者都是非常好的语言,但就范式而言,Scala 的 OO 比 Frege 好,但 Frege 的功能比 Scala 好。关于差异,它主要归结为 Haskell 与 Scala,因为 Frege 是(或几乎,参见 Haskell 和 Frege 之间的差异)用于 JVM 的 Haskell。
Frege 的类型推断是全局的,因此我们不必像在 Scala 中那样频繁地注释类型(局部推断)。
在 Frege 中,模块只是类型和函数的命名空间,而 Scala 有更好的模块系统。http://2013.flatmap.no/spiewak.html
在 Frege 中,默认情况下函数是柯里化的,因此不需要额外的构造来应用部分函数。部分类型构造函数应用程序也是如此。
在弗雷格,没有def
vs val
,一切都是函数。因此,函数比 Scala 更一流。
Frege 没有子类型,但类型系统在本机调用中计算出子类型。例如,您可以将 an 传递ArrayList
给需要 Java 的函数List
。
由于没有子类型化,在 Frege 中我们目前无法扩展 Java 类或实现接口(将来可能会支持),因此我们需要一个 Java 类来扩展/实现,但方法实现将从 Frege 传递作为函数。
在 Scala 中,调用 Java 很容易,但在 Frege 中,Java 类/方法必须在使用前声明(只是类型和纯度注释)。例如,要使用 Java 的LinkedList
,
data LinkedList a = native java.util.LinkedList where
native add :: Mutable s (LinkedList a) -> a -> ST s Bool
native get :: Mutable s (LinkedList a) -> Int -> ST s (Maybe a) throws
IndexOutOfBoundsException
native new :: () -> STMutable s (LinkedList a)
在这里,由于函数会改变对象,所以它们必须在ST
monad 中。另请注意,此处 Frege 还处理null
从get
方法返回的内容,因为它使用Maybe
类型进行了注释。进入您的 Frege 程序的唯一方法null
是通过本机接口,因为 Frege 没有 null 的概念。
另一个例子:
pure native floor Math.floor :: Double -> Double
这表明该函数是纯函数,因此签名直接反映了原始 Java 签名,没有IO
or ST
。
Frege 没有 Scala 中的变量,var
并且副作用通过类型更加明确。(只是 no null
, novar
和显式的副作用让 Frege 更有趣,至少对我来说。从某种意义上说,Frege 就像 Haskell 一样,是 JVM 的“良好的命令式编程语言”!)
作为 Haskell 方言,Frege 对 Functors、Applicatives、Monads 和其他功能“模式”更自然,并且在它的标准库中包含这些,而在 Scala 中,您可能需要 Scalaz。
Frege 默认是惰性的,但可以在必要时启用严格性,!
而 Scala 默认情况下是严格的,但具有lazy
用于惰性求值的关键字。
然而,作为 JVM 语言,一种语言可以从另一种语言中受益。我曾经将一个 Akka 示例移植到 Frege。最后,它归结为严格性、纯度、功能性、OO 和类型推断以及它们对您的重要性。
除了语法问题,最大的区别在于类型系统和执行模型。
正如@senia 已经指出的那样,scala 是严格的而不是纯函数,这并不意味着您不能编写纯函数(您也可以在 C 中这样做),只是编译器不会强制执行它。
Frege, OTOH 是懒惰和纯粹的,这意味着所有不纯的效果都被迫生活在 ST 或 IO 单子中。类型系统是 Haskell 2010 必不可少的,具有类型类和其他更高级别的函数类型。类型推断适用于整个程序,唯一的例外是具有更高级别类型的函数,其中至少必须注释多态参数。这是一个例子:
both f xs ys = (f xs, f ys)
对于这个函数,编译器推断类型:
both :: (α->β) -> α -> α -> (β, β)
请注意,两者都xs
得到ys
相同的类型,因为f
. 但是现在假设我们想要使用一个多态列表函数,我们可以将它与不同类型的 xs 和 ys 一起使用。例如,我们想写:
both reverse [1,2,3] ['a' .. 'z']
就目前而言,这个应用程序会出错,因为这两个列表有不同的元素类型,因此也有不同的类型。所以编译器会拒绝字符列表。
幸运的是,我们可以通过类型注释更准确地告诉编译器我们想要什么:
both :: (forall e.[e] -> [e]) -> [a] -> [b] -> ([a], [b])
这告诉了以下内容:我们将传递给both
一个执行一些列表转换但不关心列表元素类型的函数。然后我们传递 2 个可能具有不同元素类型的列表。我们得到一个带有我们转换列表的元组。请注意,both
无需更改代码。
另一种实现相同目的的方法是编写:
both (f :: forall e.[e]->[e]) xs ys = (f xs, f ys)
类型检查器推断其余部分,即xs
和ys
必须是列表,但可以有不同的元素类型。
Scalas 类型系统(据我所知)完全支持 OO。虽然 Frege 仅部分支持为 Java 导入的类型,但不支持定义自己的类似 OO 的类型。
因此,两种语言都支持 JVM 环境中的函数式编程,尽管处于完全不同的领域。第三个利基是动态类型,Clojure 在 JVM 世界中是王者。
也许它跑题了,但 Scala 可用于开发 Android 应用程序(*),但 Frege 尚未成功用于此。(**)IIRC,这是因为与现有 Java 库的互操作在 Scala 中比在在弗雷格等问题中。
(*) 买者自负。我自己只做过一些小的示例程序。
(**) Frege/Java 混合已用于 Android 应用程序,但仅 Frege 的应用程序仍然不可用,AFAIK。