在元编程中混淆F#语录和模式匹配

时间:2010-11-30 19:54:26

标签: f# metaprogramming

1-我真的很混淆应用F#Quotation&关于元编程的模式,请建议在F#中使用某种方法来处理这个概念。

2-你能告诉我元编程中F#语录和模式的一些实际应用吗?

3-有些人说他甚至可以用F#制作另一种语言如IronScheme,是吗?

感谢。

2 个答案:

答案 0 :(得分:28)

  

1-我真的很混淆应用F#Quotation&关于元编程的模式,请在F#中提出一些方法来解决这个概念。

引用机制允许您在代码中嵌入代码,并让编译器将您提供的源代码转换为代表它的数据结构。例如,下面给出了一个表示F#表达式1+2的数据结构:

> <@ 1+2 @>;;
val it : Quotations.Expr<int> =
  Call (None, Int32 op_Addition[Int32,Int32,Int32](Int32, Int32),
      [Value (1), Value (2)])
    {CustomAttributes = [NewTuple (Value ("DebugRange"),
          NewTuple (Value ("stdin"), Value (3), Value (3), Value (3), Value (6)))];
     Raw = ...;
     Type = System.Int32;}

然后,您可以破解此数据结构,以便将转换应用于您的代码,例如将其从F#转换为Javascript,以便在几乎任何浏览器上运行客户端。

  

2-你能告诉我元编程中F#语录和模式的实际应用吗?

与OCaml和Lisp等语言的引用机制相比,F#引用机制的功能非常有限,我想知道它为何被添加。此外,虽然.NET Framework和F#编译器提供了全速编译和执行引用代码所需的一切,但引用代码的评估机制比实际F#代码慢几个数量级,这再次使它几乎无用。因此,我不熟悉Websharper以外的任何实际应用。

例如,您只能在F#中引用某些类型的表达式而不能引用其他代码,例如类型定义:

> <@ type t = Int of int @>;;

  <@ type t = Int of int @>;;
  ---^^^^

C:\Users\Jon\AppData\Local\Temp\stdin(4,4): error FS0010: Unexpected keyword 'type' in quotation literal

大多数引用机制都允许您引用任何有效代码。例如,OCaml的报价机制可以引用F#刚刚禁止的类型定义:

$ ledit ocaml dynlink.cma camlp4oof.cma
        Objective Caml version 3.12.0

        Camlp4 Parsing version 3.12.0

# open Camlp4.PreCast;;

# let _loc = Loc.ghost;;
val _loc : Camlp4.PreCast.Loc.t = <abstr>

# <:expr< 1+2 >>;;
- : Camlp4.PreCast.Ast.expr =
Camlp4.PreCast.Ast.ExApp (<abstr>,
  Camlp4.PreCast.Ast.ExApp (<abstr>,
  Camlp4.PreCast.Ast.ExId (<abstr>, Camlp4.PreCast.Ast.IdLid (<abstr>, "+")),
  Camlp4.PreCast.Ast.ExInt (<abstr>, "1")),
  Camlp4.PreCast.Ast.ExInt (<abstr>, "2"))

# <:str_item< type t = Int of int >>;;
- : Camlp4.PreCast.Ast.str_item =
Camlp4.PreCast.Ast.StSem (<abstr>,
  Camlp4.PreCast.Ast.StTyp (<abstr>,
  Camlp4.PreCast.Ast.TyDcl (<abstr>, "t", [],
    Camlp4.PreCast.Ast.TySum (<abstr>,
    Camlp4.PreCast.Ast.TyOf (<abstr>,
      Camlp4.PreCast.Ast.TyId (<abstr>,
      Camlp4.PreCast.Ast.IdUid (<abstr>, "Int")),
      Camlp4.PreCast.Ast.TyId (<abstr>,
      Camlp4.PreCast.Ast.IdLid (<abstr>, "int")))),
    [])),
  Camlp4.PreCast.Ast.StNil <abstr>)

FWIW,这是Common Lisp中的一个例子:

$ sbcl
This is SBCL 1.0.29.11.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* '(+ 1 2)

(+ 1 2)

元编程是一种应用程序,其中模式匹配非常有用,但模式匹配是一种通用语言功能。您可能会感谢my article from the Benefits of OCaml about a minimal interpreter。特别要注意模式匹配是如何容易地对每种不同的表达式起作用的:

> let rec eval vars = function
    | EApply(func, arg) ->
        match eval vars func, eval vars arg with
        | VClosure(var, vars, body), arg -> eval ((var, arg) :: vars) body
        | _ -> invalid_arg "Attempt to apply a non-function value"
    | EAdd(e1, e2) -> VInt (int(eval vars e1) + int(eval vars e2))
    | EMul(e1, e2) -> VInt (int(eval vars e1) * int(eval vars e2))
    | EEqual(e1, e2) -> VBool (eval vars e1 = eval vars e2)
    | EIf(p, t, f) -> eval vars (if bool (eval vars p) then t else f)
    | EInt i -> VInt i
    | ELetRec(var, arg, body, rest) ->
        let rec vars = (var, VClosure(arg, vars, body)) :: vars in
        eval vars rest
    | EVar s -> List.assoc s vars;;
val eval : (string * value) list -> expr -> value = <fun>

该OCaml文章被用作F#.NET期刊文章"Language-oriented programming: The Term-level Interpreter" (31st December 2007)的基础。

  

3-有些人说他甚至可以用F#制作另一种语言如IronScheme,是吗?

是的,您可以在F#中编写编译器。事实上,F#源自一系列专门为元编程设计的语言,即所谓的MetaLanguages(ML)系列。

来自F#.NET Journal的文章"Run-time code generation using System.Reflection.Emit" (31st August 2008)描述了一个名为Brainf * ck的最小语言的简单编译器的设计和实现。您可以扩展它以实现更复杂的语言,如Scheme。实际上,F#编译器主要是用F#编写的。

在相关的说明中,我刚刚完成了一个编写高性能序列化代码的项目,该代码使用反射来消耗项目中的F#类型,然后吐出F#代码来序列化和反序列化这些类型的值

答案 1 :(得分:14)

F#引用允许您标记一些F#代码并获取源代码的表示。这是在WebSharper中使用(例如参见this tutorial)将F#代码转换为JavaScript。另一个例子是对LINQ的F#支持,其中标记为<@ ... @>的代码被转换为SQL:

let res = <@ for p in db.Products 
               if p.IsVisible then yield p.Name @> |> query

模式匹配只是一种非常强大的语言结构,但它并不比例如if更神秘。我们的想法是,您可以将值与模式匹配,程序将选择第一个匹配的分支。这很有用,因为模式可以嵌套,因此您可以使用它来处理各种复杂的数据结构或实现符号处理:

match expr with
| Multiply(Constant 0, _) | Multiply(_, Constant 0) -> 0
| Multiply(expr1, expr2) -> (eval expr1) * (eval expr2)
// (other patterns)

例如,这里我们使用模式匹配来评估数值表达式的一些表示。第一种模式是一种优化,用于处理乘法的一个参数为0的情况。

编写语言您可以使用F#(就像任何其他通用语言一样)为其他语言编写编译器和工具。在F#中,这很容易,因为它带有用于生成词法分析器和解析器的工具。请参阅示例this introduction