首页 > 解决方案 > 我怎样才能使这个函数的签名更精确

问题描述

我有两个功能:

prompt :: Text -> (Text -> Either Text a) -> IO a
subPrompt :: Text -> (Text -> Bool) -> IO a -> IO (Maybe (Text, a))

subPrompt 接受第二个提示(参数 3),如果参数 2 中的函数在运行第一个提示后返回为真,则显示它。

我不喜欢的是论点 3 是IO a我希望它更像:

subPrompt :: Text -> (Text -> Bool) -> prompt -> IO (Maybe (Text, a))

但我知道我不能那样做。我一直在想办法让签名更清楚第三个参数是什么。有什么方法可以定义更清晰的类型吗?或者也许我想多了,IO a实际上很好 - 我对haskell很陌生。

标签: haskelltypes

解决方案


一种方法是将这两件事物化为数据结构。所以:

{-# LANGUAGE GADTs #-}

data Prompt a where
    Prompt :: Text -> (Text -> Either Text a) -> Prompt a
    SubPrompt :: Text -> (Text -> Bool) -> Prompt a -> Prompt (Maybe (Text, a))

现在,因为 to 的第三个参数SubPrompt是 a Prompt,所以您知道它必须是对SubPrompt或的调用Prompt- 绝对不是IO可能进行文件系统访问或其他一些讨厌的事情的任意操作。

然后你可以为这个微型 DSL 编写一个解释器IO

runPrompt :: Prompt a -> IO a
runPrompt (Prompt cue validator) = {- what your old prompt used to do -}
runPrompt (SubPrompt cue deeper sub) = {- what your old subPrompt used to do,
                                          calling runPrompt on sub where needed -}

除了确保您没有任意IO作为参数SubPrompt的好处之外,这还有一个好处是它使测试更容易。稍后您可以实现第二个完全纯的解释器;比如说,像这样的东西,它接受一个文本列表作为用户输入,并返回一个提示输出的文本列表:

data PromptResult a = Done a | NeedsMoreInput (Prompt a)

purePrompt :: Prompt a -> [Text] -> ([Text], PromptResult a)
purePrompt = {- ... -}

推荐阅读