Scala和Haskell中的“复合”代数数据类型

时间:2012-08-21 12:51:24

标签: scala haskell algebraic-data-types

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

  1. 我是否正确无法在Haskell中表示具有“子类型”Sql且具有构造函数Statement的类型Select等模型? (似乎this可能相关)。
  2. 如果是这样,术语“ADT”是否适用于我制作的代码?
  3. 如果是的话,这会让Scala在这个方面实际上比Haskell更强大吗?
  4. 如果没有,那么在Haskell中不能实现此功能的原因是什么?这让我觉得我的模型中的东西可能过于复杂了
  5. 这是我正在谈论的模型:

    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
    

3 个答案:

答案 0 :(得分:10)

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

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。由于Scala在语言中缺少ADT,因此ADT一词永远不适用于Scala代码。但是,您所呈现的类与ADT的行为类似,所以我说“足够接近。”

3& 4,这些语言有不同的优点和缺点。

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

Haskell中不需要对象和继承,并且Scala中大多不需要ADT,因此没有理由比较这两者。大多数面向对象的功能也可以使用简单的Haskell类型类和数据类型以及使用模块边界进行模拟。可以使用案例类在Scala中模拟ADT,并且可以使用隐式参数和隐式对象实例模拟Haskell类型类。

但是,我会说它通常更容易模拟Haskell中的某些Scala功能,因为Haskell允许比Scala更多的“隐式语言扩展”。我的意思是,如果你想在Scala中模拟Haskell Monad,你必须在使用Monad的部分编写大量代码,而如果你想模仿,那就让我们比如说,Scala在Haskell中的分隔连续或隐式参数,你可以简单地为此创建一个Monad实例(用于continuation)或多个param类型类(用于隐式参数),以及稍后写入的代码您的实际功能看起来非常接近Scala代码而没有太多锅炉板。许多(如果不是大多数)Scala的高级功能也来自Haskell或OCaml,因此它们已经存在并且不需要翻译。

换句话说:添加新功能所需的复杂代码只需要添加到Haskell中的一个位置,之后它可以非常容易地在多个位置使用,而您经常需要添加大量“噪音”如果要模拟Haskell功能,请在Scala代码中的任何位置使用。

答案 1 :(得分:5)

你可以模仿Haskell中的Scala设计,尽管Haskell鼓励使用限定类型和求和类型而不是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

答案 2 :(得分:0)

问题4的一个可能答案:

Haskell的类型推断(Hindley-Milner)在存在继承时会出现问题,因此Scala具有继承但是功能较弱的类型推理器。