【问题标题】:Type functions at run-time in Idris在 Idris 中运行时键入函数
【发布时间】:2018-12-18 03:22:42
【问题描述】:

我正在讨论类型驱动的开发,并且有一个关于类型良好的printf 的部分,其中类型是从第一个参数计算出来的,它是一个格式字符串:

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

toFormatList 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 是否将此类函数视为特例?

【问题讨论】:

    标签: types idris


    【解决方案1】:

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

    对于您的情况,您必须在运行时检查 PrintfType (toFormat (unpack fmt)) 是否为 String -&gt; 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
    

    然后我们可以用toFormat (unpack fmt) 调用strFmt,并且必须同时处理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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-05
      • 1970-01-01
      • 2021-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多