将Scala Type示例转换为Haskell

Nig*_*nns 10 haskell types functional-programming scala

我在Scala文章中发现了一个非常有趣的例子,我想知道它是如何在Haskell中编码的.

trait Status
trait Open extends Status
trait Closed extends Status

trait Door[S <: Status]
object Door {
  def apply[S <: Status] = new Door[S] {}

  def open[S <: Closed](d: Door[S]) = Door[Open]
  def close[S <: Open](d: Door[S]) = Door[Closed]
}

val closedDoor = Door[Closed]
val openDoor = Door.open(closedDoor)
val closedAgainDoor = Door.close(openDoor)

//val closedClosedDoor = Door.close(closedDoor) // fails to compile
//val openOpenDoor = Door.open(openDoor) // fails to compile
Run Code Online (Sandbox Code Playgroud)

此示例在类型级别进行编码,您只能打开一个已关闭的Door,只能关闭一个打开的Door.我的第一次尝试只是使用简单的数据类型,但不能按预期工作:

data Status = Open | Closed deriving (Show)
data Door = Door Status deriving (Show)

open :: Door -> Door
open (Door Closed) = Door Open

close :: Door -> Door
close (Door Open) = Door Closed

main = do
  let closedDoor = (Door Closed)
  let openDoor = open closedDoor
  let closedAgainDoor = close openDoor
  let closeClosedDoor = close closedDoor
  let openOpenedDoor = open openDoor
  print closedAgainDoor
Run Code Online (Sandbox Code Playgroud)

这实际上编译(除非我尝试打印closeClosedDooropenOpenedDoor然后抱怨功能打开的非详尽模式,这是显而易见的)

所以我试图弄清楚GADT我们的类型家族是否可以完成这项任务,但我还不知道如何.

有任何想法吗?

Ale*_*lec 18

除了bheklilr的答案,你可以使用一些类型扩展来更接近Scala示例并排除非敏感类型,如

Door String
Run Code Online (Sandbox Code Playgroud)

使用DataKinds,你可以有效地禁止幻像类型,除了一个Status.

{-# LANGUAGE DataKinds #-}

data Door (status :: Status) = Door
data Status = Open | Closed

open :: Door Closed -> Door Open
open _ = Door

close :: Door Open -> Door Closed
close _ = Door
Run Code Online (Sandbox Code Playgroud)

然后,对于类型系列,我们甚至可以定义"切换"门的含义

{-# LANGUAGE TypeFamilies #-}

type family Toggle (s :: Status) where
  Toggle Open = Closed
  Toggle Closed = Open

toggle :: Door s -> Door (Toggle s)
toggle Door = Door
Run Code Online (Sandbox Code Playgroud)

作为结束思想,使用GADT可能更好Door- 只是因为你有两个不同的构造函数名称.我个人认为这看起来更好

{-# LANGUAGE GADTs, DataKinds, TypeFamilies #-}

data Door (status :: Status) where
  OpenDoor :: Door Open
  ClosedDoor :: Door Closed

open :: Door Closed -> Door Open
open _ = OpenDoor

close :: Door Open -> Door Closed
close _ = ClosedDoor

toggle :: Door s -> Door (Toggle s)
toggle OpenDoor = ClosedDoor
toggle ClosedDoor = OpenDoor
Run Code Online (Sandbox Code Playgroud)

  • @Alec,它肯定是一个更普遍模式的特例!当你的类型族是单射的时候,你应该总是考虑它.但即使不是,也要寻找机会以一个或多个其他方式约束每个类型变量. (2认同)

bhe*_*ilr 10

我会做点什么的

data Open = Open deriving (Show)
data Closed = Closed deriving (Show)
data Door door_state = Door deriving (Show)

open :: Door Closed -> Door Open
open _ = Door

close :: Door Open -> Door Closed
close _ = Door
Run Code Online (Sandbox Code Playgroud)

现在没有任何情况需要考虑,状态本身是在类型中编码的,就像Scala示例一样.