"Composite" Scala 和 Haskell 中的代数数据类型



在Scala中尝试用代数数据类型描述Sql的一部分时,我遇到了创建表示数据类型的根特征的子数组的必要性。由于满足这一要求产生了一个我不确定是否可以用Haskell的ADT表示的代码,而且与Haskell不同的是,ADT不是Scala的原生构造,我现在想知道:

  1. 我说得对吗,在Haskell中不可能表示一个模型,比如一个类型Sql,它有一个"子类型"Statement,它有构造函数Select(看起来这可能有关系)
  2. 如果是,术语"ADT"是否适用于我生成的代码
  3. 如果是的话,这是否真的使Scala在这方面比Haskell更强大
  4. 如果没有,那么不在Haskell中实现此功能的原因是什么?这让我觉得我的模型可能过于复杂了

这是我所说的模型:

sealed trait Sql

sealed trait Statement 
  extends Sql
sealed case class Union 
  ( left : Statement, 
    right : Statement ) 
  extends Statement
sealed case class Select
  ( /** other fields don't matter **/
    where : Where )
  extends Statement

sealed trait Where 
  extends Sql
sealed case class And
  ( left : Where,
    right : Where )
  extends Where
sealed case class Or
  ( left : Where,
    right : Where )
  extends Where
sealed case class Equals
  ( /** fields don't matter **/ )
  extends Where

1。不,由于根特征是密封的,因此可以将所呈现的层次结构表示为ADTs:

data Sql = Statement Statement | Where Where
        --           ^ This is the *type* called `Statement`
        -- ^ This is the *constructor* called `Statement`
data Statement = Union Statement Statement | Select Where
data Where = And Where Where | Or Where Where | Equals

在这种情况下,这是可能的,因为您可以枚举数据类型的所有"子类"(在本例中为Sql),这使得将它们转换为ADT构造函数成为可能。只有当您想允许用户任意添加"构造函数"/"子类"时,才很难将类型层次结构模拟为ADT。

2.术语ADT永远不适用于Scala代码,因为Scala在语言中缺少ADT。然而,您介绍的类的行为与ADT类似,所以我认为"足够接近"

3&4.语言有不同的长处和短处。

Haskell可以模拟Scala语言的每一个特性,Scala可以模拟Haskell的每一种特性,因为这两种语言都是完全的,并且允许不同级别的元编程。Haskell当然有TemplateHaskell,它允许模拟任何东西——您可能可以使用TH在Haskell文件中编写Scala代码,并将其编译为Haskell。

Haskell中不需要对象和继承,Scala中也不需要ADT,所以没有理由将两者进行比较。大多数面向对象的特性也可以用简单的Haskell类型类和数据类型以及使用模块边界来模拟。ADT可以在Scala中用case类来模拟,Haskell类型的类可以用隐式参数和隐式对象实例来模拟。

然而,我想说的是,在Haskell中模拟某些Scala功能通常更容易,因为Haskell比Scala允许更多的"隐式语言扩展"。我的意思是,如果你想在Scala中模拟Haskell Monads,你必须在使用Monads的部分中编写大量代码,而如果你想模拟,比如说,在Haskell中模拟Scala的定界延续或隐式参数,你可以简单地写一个Monad实例(用于延续)或多参数类型类(用于隐式参数),以后在实际函数中编写的代码看起来非常接近Scala代码,没有太多的锅炉板。Scala的许多高级功能(如果不是大多数的话)也来自Haskell或OCaml,所以它们已经存在,不需要翻译。

换句话说:添加新功能所需的复杂代码只需添加到Haskell中的一个位置,之后就可以很容易地在多个位置使用,而如果你想模仿Haskell功能,你通常必须在Scala代码中到处添加很多"噪音"。

您可以在Haskell中模仿Scala的设计,尽管Haskell鼓励使用限定类型和sum类型,而不是Scala的继承和子类型。由于我对Scala的了解有限,我在下面的Haskell中写了同样的东西。

  • 特质就是阶级
  • 案例类别为ADT
  • 特性继承是类成员身份
  • 特征类型成员是有界存在类型
  • Typeable类用于下广播
-- Define traits
class Typeable a => Sql a
class (Typeable a, Sql a) => Statement a
class (Typeable a, Sql a) => Where a
-- Define case classes
data Union  where Union     :: (Statement a, Statement b) => a -> b -> Union deriving (Typeable)
data Select where Select    :: Where a => a -> Select                        deriving (Typeable)          
data And    where And       :: (Where a, Where b) => a -> b -> And           deriving (Typeable)
data Or     where Or        :: (Where a, Where b) => a -> b -> Or            deriving (Typeable)
data Equals where Equals    :: Equals                                        deriving (Typeable)
-- Define subtyping
instance Sql Union
instance Statement Union
instance Sql Select
instance Statement Select
instance Sql And
instance Where And
instance Sql Or
instance Where Or
instance Sql Equals
instance Where Equals

在Haskell中,您可能会使用sum类型,而不是StatementWhere类。

class Sql a
instance Sql Statement
instance Sql Where
data Statement = Union Statement Statement | Select Where
data Where = And Where Where | Or Where Where | Equals

问题4的一个可能答案:

Haskell的类型推理(Hindley-Milner)在存在继承的情况下会有问题,所以Scala有继承,但它的类型推理能力较弱。

最新更新