将Scala Type示例转换为Haskell

时间:2016-12-02 19:21:05

标签: scala haskell types functional-programming

我在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

此示例在类型级别进行编码,您只能打开已关闭的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

这实际上是编译的(除非我尝试打印closeClosedDooropenOpenedDoor然后抱怨函数打开时非详尽的模式,这很明显)

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

有什么想法吗?

2 个答案:

答案 0 :(得分:18)

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

shared_examples "admin common actions" do |resource|
  describe "logged admin" do
    login_admin

    describe 'GET #index' do
      it 'responds with 200' do
        get :index, :format => :html
        expect(response).to have_http_status(200)
      end

      context 'when rendering view' do
        render_views

        it 'renders the view' do
          expect { get :index, :format => :html }.to_not raise_error
        end
      end
    end
  end
end

使用Door String ,您可以有效地禁止幻像类型只是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

作为结束的想法,为{-# LANGUAGE TypeFamilies #-} type family Toggle (s :: Status) where Toggle Open = Closed Toggle Closed = Open toggle :: Door s -> Door (Toggle s) toggle Door = Door 使用GADT可能更好 - 只是因为你有两个不同的构造函数名称。我个人认为这读得更好

Door

答案 1 :(得分: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

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