Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations code as-data for f#

1,215 views

Published on

Code Quotations: Code-as-Data for F#
This tutorial will cover F# Code Quotations in-depth. You'll learn what Code Quotations are, how to use them, and where to apply them in your applications. We'll work through several real-world examples to highlight the important features -- and potential pitfalls -- of Code Quotations.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,215
On SlideShare
0
From Embeds
0
Number of Embeds
58
Actions
Shares
0
Downloads
8
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations code as-data for f#

  1. 1. F# Quotations: “Code as Data” Jack Pappas Dmitry Morozov SkillsMatter Progressive F# Tutorials NYC September 19th, 2013
  2. 2. Code as Data • Code & Data – What is the difference? • Is there a difference? • Programs exist to process data – The F# compiler processes data (your source code) • Source code is no different than other kinds of data
  3. 3. This Session • What is a “quoted expression”? – A simple, immutable data structure – Represents a fragment of F# code • Syllabus – Background – Working with Quotations – Manipulating Quotations – Applications of Quotations
  4. 4. Background • The idea of treating code as data first appeared in Lisp Lisp F# Code (1 2 3) [1; 2; 3] Code as Data ‘(1 2 3) <@ [1; 2; 3] @>
  5. 5. Background • A language that has the natural ability to manipulate itself is extremely powerful • “Amplify” a developer’s productivity using metaprogramming techniques
  6. 6. Background • Other languages can manipulate their own code – clang: C++ program, manipulates C++ – Roslyn: C# library, manipulates C# – f2c: C program, manipulates FORTRAN and C • What makes Lisp and F# different? – Reflective meta-programming – Built-in language feature vs. external functionality
  7. 7. Background • Now that we can manipulate code as data, what can we do with it? • Transform – modify, convert, translate • Analyze – Static analysis – Advanced testing techniques (e.g., verification) – Collect and record code metrics (e.g., complexity)
  8. 8. Background • Execute – Interpret the code/data – Translate to another lang., compile, execute – Specialized hardware (e.g., GPU, FPGA) – Specialized execution (e.g., auto-parallelization) – Execute remotely (e.g., web service) – Some combination of the above – (Rarely) execute in same context, e.g., by translating to IL and executing
  9. 9. Use Cases • LINQ support in F# – Extension of this idea: Cheney, Lindley, Wadler. “The essence of language-integrated query” • Use as an IL when developing type providers – Quotations much easier to work with – Create quotations, translate to IL
  10. 10. Use Cases • Testing – Moq/Foq/Unquote • GPU programming – Compute: FSCL, QuantAlea – Graphics: SharpShaders (F# -> HLSL) • Web Development (F# -> JavaScript) – WebSharper – FunScript – PitFw
  11. 11. Comparing Alternatives • What alternatives exist to quotations? – F# AST – LINQ Expression Trees – CIL (MSIL) • Imperative: Expression Trees, CIL – C# lambdas compiled into Expression Trees by the C# compiler • Functional: F# AST, Quotations
  12. 12. Comparing Alternatives • F# AST – Designed for the compiler’s internal use – Carries more information than quotations – Requires additional library – Unwieldy for simple use cases
  13. 13. Comparing Alternatives • LINQ Expression Trees – Introduced in .NET 3.5; initially somewhat limited – Expanded in .NET 4.0; now roughly 1:1 with CIL – Doesn’t support certain functional features like closures
  14. 14. Comparing Alternatives • Common Intermediate Language (CIL) – Low-level; relatively difficult to work with – Supports all CLR features – F# Quotations don’t support all CLR features ● Notably, throw and rethrow – these need to be wrapped in a function and compiled using the proto compiler ● No fault/filter clauses • F# Quotations don’t support open generics
  15. 15. Quotations: How-To • Open some namespaces • Define a module, then some functions within the module open System.Reflection open Microsoft.FSharp.Reflection open Microsoft.FSharp.Quotations module Foo = let addTwo x = x + 2
  16. 16. Quotations: How-To • Apply [<ReflectedDefinition>] to your function – Or, apply it to your module as a shortcut for applying it to all functions in the module module Foo = /// Dummy class used to retrieve /// the module type. type internal Dummy = class end [<ReflectedDefinition>] let addTwo x = x + 2
  17. 17. Quotations: How-To • Using Reflection, get the MethodInfo for your function • Call Expr.TryGetReflectedDefinition with the MethodInfo to get the Expr for the method
  18. 18. Quotations: How-To [<EntryPoint>] let main argv = let fooType = typeof<Foo.Dummy>.DeclaringType let addTwoMethodInfo = match fooType.GetMethod "addTwo" with | null -> None | x -> Some x addTwoMethodInfo |> Option.map Expr.TryGetReflectedDefinition |> Option.iter (printfn "addTwo: %A")
  19. 19. Quotations: How-To • For each active pattern in Quotations.Patterns, there is a corresponding static member of the Expr type. – These are used to construct and destructure each quotation “case”. • Two types of quotations: typed and untyped – Typed: Expr<‘T> – Untyped: Expr
  20. 20. Quotations: How-To • Expr<‘T> is just a wrapper around Expr – Use the .Raw property for Expr<‘T> -> Expr – Use the Coerce pattern for Expr -> Expr<‘T> • Quoting an expression – Typed: <@ fun x -> x + 2 @> – Untyped: <@@ fun x -> x + 2 @@>
  21. 21. Quotations: How-To • “Splicing” – a way to combine quotations – Inserts one quotation into another – Typed: let bar = <@ 3 + "Blah".Length @> let foo = <@ fun x -> x + %bar @> – Untyped: let bar = <@@ 3 + "Blah".Length @@> let foo = <@@ fun x -> x + %%bar @@>
  22. 22. Quotations: How-To • Note: You don’t always need [<ReflectedDefinition>] to use quotations • Using our previous example, this is equivalent let addTwo = <@ fun x -> x + 2 @>
  23. 23. Computations on Trees • When working with Quotations, it’s critical you’re comfortable with trees and recursion • Tree computations in imperative languages usually built around the “visitor” pattern – Visitor class usually holds private, mutable state – Often implemented as an abstract base class per tree type, and one virtual method per node type – Mechanics of the traversal intermixed with computation happening at nodes
  24. 24. Computations on Trees • Possible to implement a functional alternative to the “visitor” pattern? • Knuth did – decades ago! • Introducing “attribute grammars” – Initially used as a means of describing how LL/LR parsers annotate a parse tree – Later, used to describe how “attributes” are computed from on tree
  25. 25. Computations on Trees • Attribute grammars are declarative – They do not define any traversal order • Attribute grammars allow you to define purely functional, monadic computations over trees – For those familiar with Haskell – attribute evaluation is actually co-monadic • AGs on ASTs (or Quotations) can be used to implement purely functional AOP
  26. 26. Computations on Trees • Three main kinds of attributes • Inherited (L-attributes) – Attribute “inherited” from parent node – Compare to the reader workflow • Synthesized (S-attributes) – Attribute “synthesized” by a child node, passed back up to parent node – Compare to the writer workflow
  27. 27. Computations on Trees • Threaded (SL-attributes) – Attribute value inherited from parent node, modified by child, then passed back up to parent – Combination of Inherited and Synthesized – Compare to the state workflow • Attribute values can be int, string, UDTs, etc. • They can also be tree nodes – We can use this to define tree transformations
  28. 28. Exercises • Implement a function(s) which, given an Expr: – Print the equiv. F# source code – Invert the mathematical operators (+)/(-), (*)/(/) – Compute the set of methods called by the Expr ● Bonus: Compute this transitively – Determine if the Expr can ever raise an exn – Determine if the Expr is pure (side-effect free) – Rewrite it so all strings are passed through String.Intern

×