如何在类型中编码可能的状态转换?

2023-12-26

我试图在 Haskell 中复制这段 Idris 代码,它通过类型强制执行正确的操作顺序:

 data DoorState = DoorClosed | DoorOpen
 data DoorCmd : Type ->
                DoorState ->
 DoorState ->
                Type where
      Open : DoorCmd     () DoorClosed DoorOpen
      Close : DoorCmd    () DoorOpen   DoorClosed
      RingBell : DoorCmd () DoorClosed DoorClosed
      Pure : ty -> DoorCmd ty state state
      (>>=) : DoorCmd a state1 state2 ->
              (a -> DoorCmd b state2 state3) ->
              DoorCmd b state1 state3

由于超载(>>=)运算符,可以编写如下一元代码:

do Ring 
   Open 
   Close

但编译器拒绝不正确的转换,例如:

do Ring
   Open 
   Ring
   Open

我尝试在以下 Haskell 片段中遵循这种模式:

 data DoorState = Closed | Opened

 data DoorCommand (begin :: DoorState) (end :: DoorState) a where
   Open  :: DoorCommand 'Closed 'Opened ()
   Close :: DoorCommand 'Opened 'Closed ()
   Ring  :: DoorCommand 'Closed 'Closed ()

   Pure  :: x -> DoorCommand b e x
   Bind  :: DoorCommand b e x -> (x -> DoorCommand e f y) -> DoorCommand b f y

 instance Functor (DoorCommand b e) where
   f `fmap` c = Bind c (\ x -> Pure (f x))

 -- instance Applicative (DoorCommand b e) where
 --    pure    = Pure
 --    f <*> x = Bind f (\ f' -> Bind x (\ x' -> Pure (f' x')))

 -- instance Monad (DoorCommand b e) where
 --   return = Pure
 --   (>>=) = Bind

但这当然失败了:Applicative and Monad无法正确定义实例,因为它们需要两个不同的实例才能正确排序操作。构造函数Bind可以用来强制执行正确的排序,但我无法设法使用“更好的”do 表示法。

我怎样才能编写这段代码才能使用 do 表示法,例如以防止无效序列Commands ?


您要找的确实是 Atkey 的参数化单子 http://bentnib.org/paramnotions-jfp.pdf,现在更常被称为索引单子.

class IFunctor f where
    imap :: (a -> b) -> f i j a -> f i j b
class IFunctor m => IMonad m where
    ireturn :: a -> m i i a
    (>>>=) :: m i j a -> (a -> m j k b) -> m i k b

IMonad是类 monad 事物的类m :: k -> k -> * -> *通过属于该类型的类型的有向图描述路径k. >>>=绑定一个计算,该计算从以下位置获取类型级别状态i to j进行计算,将其取自j to k,返回更大的计算i to k. ireturn允许您将纯值提升到一元计算中,而不会更改类型级别状态。

我将使用索引自由单子捕获这种请求-响应操作的结构,主要是因为我不想弄清楚如何编写IMonad我自己的类型的实例:

data IFree f i j a where
    IReturn :: a -> IFree f i i a
    IFree :: f i j (IFree f j k a) -> IFree f i k a

instance IFunctor f => IFunctor (IFree f) where
    imap f (IReturn x) = IReturn (f x)
    imap f (IFree ff) = IFree $ imap (imap f) ff
instance IFunctor f => IMonad (IFree f) where
    ireturn = IReturn
    IReturn x >>>= f = f x
    IFree ff >>>= f = IFree $ imap (>>>= f) ff

我们可以打造您的Door来自以下函子的 monad 免费:

data DoorState = Opened | Closed
data DoorF i j next where
    Open :: next -> DoorF Closed Opened next
    Close :: next -> DoorF Opened Closed next
    Ring :: next -> DoorF Closed Closed next

instance IFunctor DoorF where
    imap f (Open x) = Open (f x)
    imap f (Close x) = Close (f x)
    imap f (Ring x) = Ring (f x)

type Door = IFree DoorF

open :: Door Closed Opened ()
open = IFree (Open (IReturn ()))
close :: Door Opened Closed ()
close = IFree (Close (IReturn ()))
ring :: Door Closed Closed ()
ring = IFree (Ring (IReturn ()))

You can open一扇门,导致当前关闭的门打开,close当前打开的门,或ring门铃一直关着,大概是因为房子的主人不想见到你。

最后,RebindableSyntax语言扩展意味着我们可以替换标准Monad按照我们自己的习惯上课IMonad.

(>>=) = (>>>=)
m >> n = m >>>= const n
return = ireturn
fail = undefined

door :: Door Open Open ()
door = do
    close
    ring
    open

但是我注意到您并没有真正使用单子的绑定结构。没有你的构建模块Open, Close or Ring返回一个值。所以我认为你真正需要的是以下更简单的类型对齐列表 type:

data Path g i j where
    Nil :: Path g i i
    Cons :: g i j -> Path g j k -> Path g i k

在操作上,Path :: (k -> k -> *) -> k -> k -> *就像一个链表,但它有一些额外的类型级结构,再次描述了通过有向图的路径,其节点位于k。列表的元素是边g. Nil说你总是可以找到从节点开始的路径i对自己和Cons提醒我们,千里之行始于足下:如果你有优势i to j和一条路径j to k,您可以将它们组合起来形成一条路径i to k。它被称为一个类型对齐列表因为一个元素的结束类型必须与下一个元素的起始类型匹配。

在咖喱霍华德街的另一边,如果g则为二元逻辑关系Path g构造其自反传递闭包。或者,断然地,Path g是态射的类型免费类别图表的g。在自由类别中组合态射只是(翻转)附加类型对齐列表。

instance Category (Path g) where
    id = Nil
    xs . Nil = xs
    xs . Cons y ys = Cons y (xs . ys)

然后我们可以写Door按照Path:

data DoorAction i j where
    Open :: DoorAction Closed Opened
    Close :: DoorAction Opened Closed
    Ring :: DoorAction Closed Closed

type Door = Path DoorAction

open :: Door Closed Opened
open = Cons Open Nil
close :: Door Opened Closed
close = Cons Close Nil
ring :: Door Closed Closed
ring = Cons Ring Nil

door :: Door Open Open
door = open . ring . close

你不明白do符号(虽然我think RebindableSyntax确实允许您重载列表文字),但是使用以下命令构建计算(.)看起来像是纯函数的排序,我认为这对于你正在做的事情来说是一个很好的类比。对我来说,使用索引单子需要额外的脑力——一种稀有而珍贵的自然资源。当更简单的结构就可以时,最好避免 monad 的复杂性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在类型中编码可能的状态转换? 的相关文章

  • 持久 selectList 导致错误“无法将类型‘BaseBackend backend0’与‘SqlBackend’匹配”

    我遇到以下编译错误 Couldn t match type BaseBackend backend0 with SqlBackend arising from a use of runSqlite The type variable bac
  • 获取参数类型的参数

    假设我定义了一个这样的类型 type Point Tx Ty end 然后我创建一个这种类型的变量 例如 a Point Int64 something 现在 我只知道我可以获得以下类型a by typeof a 那是 Point Int6
  • 如何在 Haskell 中向右或向左移动列表的 1 个元素?

    嗨 我一直在寻找答案 但找不到 假设我们有一个像这样的列表 1 10 4 5 3 我怎样才能将 5 向左移动 使这个列表变成 1 10 5 4 3 我尝试过了swapElementsAt通过找到该元素的索引 但它看起来非常不足 swapEl
  • 规范化且不可变的数据模型

    Haskell如何解决 规范化不可变数据结构 问题 例如 让我们考虑一个表示前女友 男友的数据结构 data Man Man name String exes Woman data Woman Woman name String exes
  • “Eta减少”并不总是在Haskell中举行?

    我发现我可以说 LANGUAGE RankNTypes f1 forall b b gt b gt forall c c gt c f1 f id f HLint 告诉我我可以在这里做 Eta 减少 但是 f2 forall b b gt
  • 以下两个 lambda 函数的空间复杂度

    我正在阅读以下内容 https en wikibooks org wiki Haskell Graph reduction https en wikibooks org wiki Haskell Graph reduction 其内容如下
  • 具有继承类型的 Aux 模式推理失败

    我有一个复杂的玩具算法 我希望纯粹在类型级别上表示 根据饮食要求选择当天菜肴的修改 对卷积表示歉意 但我认为我们需要每一层才能达到我想要使用的最终界面 我的代码有一个问题 如果我们表达一个类型约束Aux 模式生成的类型基于另一个泛型类型 它
  • Powershell日期类型无法找到

    我正在尝试使用PowerShell连接virustotal API 代码来自virustotal网站 我得到 无法找到类型 System Security Cryptography ProtectedData 错误信息 代码如下 funct
  • 标准的能力

    我发现了一些使用标准的旧例子here http www serpentine com blog 2009 09 29 criterion a new benchmarking library for haskell 看起来好像早在 2009
  • 如何使 PyCharm 从函数定义中获取类型提示并在文档字符串中填充类型值?

    我总是在函数定义中使用类型提示 例如 def foo a int b str gt bool pass 当我使用 PyCharm 自动文档字符串生成器在代码中生成文档字符串时 我得到以下信息 def foo a int b str gt b
  • C++ 强制转换运算符重载 [重复]

    这个问题在这里已经有答案了 我有一个只有一个 int 成员的类 例如 class NewInt int data public NewInt int val 0 constructor data val int operator int N
  • 如何在haskell中获取变量名称

    我来到 haskell 时有一些 c 背景知识 想知道是否有类似的 define print a printf s d n a a int a 5 print a 应该打印 a 5 这是 augustss 提到的 TH 解决方案 LANGU
  • 你能识别 Haskell 程序中的无限列表吗? [复制]

    这个问题在这里已经有答案了 可能的重复 如何判断列表是否是无限的 https stackoverflow com questions 7371730 how to tell if a list is infinite 在Haskell中 你
  • Haskell:IORef 的性能

    我一直在尝试在 Haskell 中编码一个需要使用大量可变引用的算法 但与纯粹的惰性代码相比 它 也许并不奇怪 非常慢 考虑一个非常简单的例子 module Main where import Data IORef import Contr
  • Haskell 输入返回元组

    我想知道 IO 函数是否可以返回元组 因为我想从这个函数中获取这些元组作为另一个函数的输入 investinput IO gt Char Int investinput do putStrLn Enter Username username
  • 为什么 ZipList 不是 List 的默认应用实例

    我目前正在学习 Haskell 中的应用程序 如果我没记错的话 列表有两个不同的应用实例 List and ZipList 第二个被定义为包装列表值的新类型 这ZipList应用实例对我来说似乎更直观 这可能是一个愚蠢的问题 但有具体原因吗
  • 我该如何实现这个折叠功能呢?

    给出了两种数据类型 颜色 和 植物 data Color Red Pink White Blue Purple Green Yellow deriving Show Eq data Plant Leaf Blossom Color Stal
  • Haskell 标准库是什么?

    GHC专用库可以称为标准库吗 或者只有 Haskell 2010 报告中的那些才算数 许多 GHC 库可以通过 Haskell 报告中的函数来实现 可能与 C 绑定相结合 但其他语言依赖于 GHC 特定的扩展 因为语言报告中定义的当前 Ha
  • Haskell Data.Decimal 作为 Aeson 类型

    是否可以解析一个数据 十进制 https hackage haskell org package Decimal 0 4 2 docs Data Decimal html使用 Aeson 包从 JSON 获取 假设我有以下 JSON foo
  • C++ 概念与 Haskell 类型类有何不同?

    Concepts TS 中的 C 概念最近已合并到 GCC 主干中 概念允许人们通过要求类型满足概念的条件 例如 可比较 来约束通用代码 Haskell 有类型类 我对 Haskell 不太熟悉 概念和类型类如何相关 概念 由概念 TS 定

随机推荐