.net - F# printf 编译器魔法

标签 .net compiler-construction f#

在 F# 中,编译器显然做了一些魔术来完成这项工作:

printfn "%i %i" 6 7 ;; // good
printfn "%i %i" 6 7 8;; // error

它是如何做到的?有什么方法可以在语言中实现类似的行为?

最佳答案

遗憾的是,这一点“魔法”(正如您所说的那样)被硬编码到 F# 编译器中。您可以扩展编译器,但结果将是非标准 F#。

这是处理该问题的特定代码(它不太可读,但这就是 F# 编译器的编写方式):

and TcConstStringExpr cenv overallTy env m tpenv s  =

    if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then 
      mkString cenv.g m s,tpenv
    else 
      let aty = NewInferenceType ()
      let bty = NewInferenceType ()
      let cty = NewInferenceType ()
      let dty = NewInferenceType ()
      let ety = NewInferenceType ()
      let ty' = mkPrintfFormatTy cenv.g aty bty cty dty ety
      if (not (isObjTy cenv.g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy ty') then 
        // Parse the format string to work out the phantom types 
        let aty',ety' = (try Formats.ParseFormatString m cenv.g s bty cty dty with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString(s),m)))
        UnifyTypes cenv env m aty aty';
        UnifyTypes cenv env m ety ety';
        mkCallNewFormat cenv.g m aty bty cty dty ety (mkString cenv.g m s),tpenv
      else 
        UnifyTypes cenv env m overallTy cenv.g.string_ty;
        mkString cenv.g m s,tpenv

这是同样的代码,它也支持数字字符串(即 printfn "%i %i"("4"+ 2) "5" 将类型检查并打印 6 5):

and TcConstStringExpr cenv overallTy env m tpenv s  =

    if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then 
      mkString cenv.g m s,tpenv
    elif (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.int_ty) then 
      mkInt cenv.g m (System.Int32.Parse s),tpenv
    elif (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.int32_ty) then 
      mkInt32 cenv.g m (System.Int32.Parse s),tpenv
    else 
      let aty = NewInferenceType ()
      let bty = NewInferenceType ()
      let cty = NewInferenceType ()
      let dty = NewInferenceType ()
      let ety = NewInferenceType ()
      let ty' = mkPrintfFormatTy cenv.g aty bty cty dty ety
      if (not (isObjTy cenv.g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy ty') then 
        // Parse the format string to work out the phantom types 
        let aty',ety' = (try Formats.ParseFormatString m cenv.g s bty cty dty with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString(s),m)))
        UnifyTypes cenv env m aty aty';
        UnifyTypes cenv env m ety ety';
        mkCallNewFormat cenv.g m aty bty cty dty ety (mkString cenv.g m s),tpenv
      else 
        UnifyTypes cenv env m overallTy cenv.g.string_ty;
        mkString cenv.g m s,tpenv

P.S.:我很久以前写过这个,所以我不记得为什么那里有 mkIntmkInt32 。这可能是必要的,也可能不是 - 但我确实记得这段代码有效。

关于.net - F# printf 编译器魔法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17109262/

相关文章:

c# - 返回作为 C# 中的集合的泛型类型

class - 如何让一个元组给出一个随机的新元组?

compiler-construction - 在堆垛机中处理临时物

java - 实例化自己的类时编译器如何处理Main

f# - FSharp.Core中未记录的 `when`关键字用法

f# - 哪个与缩进规则的匹配在这里起作用?

.net - WPF。 MVVM。使用事件是不好的口气吗?

c# - 将多个元素添加到 Prism 区域

.net - Win32 与 .Net

c++ - 修改 makefile