在运行时在Idris中键入函数

时间:2018-07-10 10:09:55

标签: types idris

我已经完成了类型驱动的开发,并且在类型良好的printf上有一节,其中类型是从第一个参数(格式字符串)计算得出的:

printf : (fmt : String) -> PrintfType (toFormat (unpack fmt))

toFormat根据List Char创建格式表示,而PrintfType根据此类格式表示创建函数类型。完整的代码在这里:https://github.com/edwinb/TypeDD-Samples/blob/master/Chapter6/Printf.idr

我了解printf "%c %c" 'a' 'b'之类的代码的工作-printf "%c %c的类型被计算为Char -> Char -> String

我不明白的是,在运行时第一个参数未知时会发生什么。我了解为什么通常这样的代码不应编译:如果在运行时由用户输入格式字符串和生成的类型,则在编译时无法知道它们,但是我不知道不知道这种直觉背后的实际技术推理。

例如:

main : IO ()
main = do fmt <- getLine
          c1 <- getChar
          c2 <- getChar
          printf fmt c1 c2

导致以下错误消息:

When checking an application of function Prelude.Monad.>>=:
        printf fmt does not have a function type (PrintfType (toFormat (unpack fmt)))

我想我正在尝试了解此错误消息。 Idris是否将这种功能视为特例?

1 个答案:

答案 0 :(得分:6)

printf fmt : PrintfType (toFormat (unpack fmt))的类型无法进一步评估,因为fmt : String在编译时是未知的。因此,对于Idris而言,这不是函数-它是A而不是A -> B。这就是错误消息的意思。

对于您的情况,您必须在运行时检查PrintfType (toFormat (unpack fmt))是否为String -> String

作为示例,以下两个函数采用一种格式,并可能返回一个证明其格式正确的证明:

endFmt : (fmt : Format) -> Maybe (PrintfType fmt = String)
endFmt End = Just Refl
endFmt (Lit str fmt) = endFmt fmt
endFmt fmt = Nothing

strFmt : (fmt : Format) -> Maybe ((PrintfType fmt) = (String -> String))
strFmt (Str fmt) = case endFmt fmt of
  Just ok => rewrite ok in Just Refl
  Nothing => Nothing
strFmt (Lit str fmt) = strFmt fmt
strFmt fmt = Nothing

然后我们可以用strFmt来调用toFormat (unpack fmt),并且必须处理两种Maybe的情况。由于Idris在应用证明方面存在问题,因此我们为replace提供了帮助。

main : IO ()
main = do fmt <- getLine
          str <- getLine
          case strFmt (toFormat (unpack fmt)) of
            Just ok => let printer = replace ok {P=id} (printf fmt) in
              putStrLn $ printer str
              -- in a better world you would only need
              -- putStrLn $ printf fmt str
            Nothing => putStrLn "Wrong format"

有了这个,我们可以运行:

:exec main
foo %s bar  -- fmt
and         -- str
foo and bar -- printf fmt str