fsharp / fsharp/fslang-suggestions

Splice types into Quotations

Open
#670 10 comments 8 reactions 0 assignees View on GitHub
approved-in-principle area: quotations-and-linq
Dominant language
No language data
Stars
373
Forks
21
PR merge metrics
No merged PRs in 30d

Description

Currently, quotations have difficulty when dealing creating expressions where types are not known at compile time.

In the following example, we desire a function which gives us an expression corresponding to creating an array with type `t` with length `n`. In order to do this, we must use the generic function: `Array.zeroCreate`. This is a simple implementation of the desired function:
```fsharp
let replaceTypeOnCall (t : System.Type) = function
| Call(o,mi,args) ->
let newMI = mi.GetGenericMethodDefinition().MakeGenericMethod([|t|])
match o with
| Some o -> Expr.Call(o,newMI,args)
| None -> Expr.Call(newMI, args)
| _ -> failwith "Called on a non-Call Expr"

let makeArrayExpr (length : int) (t : Type) =
<@@ Array.zeroCreate length @@>
|> replaceTypeOnCall t
```
We require an additional helper function to modify this `Expr.Call` to use the correct `System.Type`. The above example works fine, and is not too big of a pain, because there is only one call to replace, and one type parameter to replace.

An expression which adds two ints together is simple:
```fsharp
let add (x : Expr) (y : Expr) = <@@ (%% x : int) + (%% y : int) @@>
```
An expression which adds two t expressions is not possible to write using quotations.

```fsharp
let add (x : Expr) (y : Expr) (t : Type) =
let minfo = t.GetMethod("op_Addition")
Expr.Call(minfo, [x; y])
```

This proposal is to allow splicing of type values into quotations, using ~% operator like so:
```fsharp
let makeArrayExpr (length : int) (t : Type) =
<@@ Array.zeroCreate<~%t> length @@>
```
This example is simple, it instantiates the generic `Array.zeroCreate MethodInfo` with the `System.Type t`.

```fsharp
let testEquals (x : Expr) (y : Expr) (t : Type) = <@@ (%% x : ~%t) + (%% y : ~%t) @@>
```
This example is somewhat more complicated. We want to construct an expression which corresponds to the addition of `x` and `y`, using a `+` operator. This would desugar to asking for the `op_Addition MethodInfo` from the given `t`, and building an `Expr.Call` using this method.

This process can obviously fail at runtime, but we are already in a world where the %% operator exists (which can obviously fail at runtime).

Under the hood, this ~% operator maps an expression to a free type variable. For unification to succeed, each term variable `t` must map to the same type variable `'t`.

Naturally, this formulation should complain if one of these variables escapes the scope.

```fsharp
<@ (%%x : ~%t) @>
>>> Type variable 't escapes this scope.
```

Resolving operators is done as follows:
Simple constraints such as: `^t : (static member op_Addition : ^t * ^t -> ^t)` can be translated to:
```fsharp
t.GetMethod("op_Addition")
```
"Or" constraints such as `(^s or ^t) : (static member op_Addition : ^s * ^t -> ^t)` can be translated to:

```fsharp
match s.GetMethod("op_Addition"), t.GetMethod("op_Addition") with
| null, null -> failwith "No method found"
| mi, null -> mi
| null, mi -> mi
| mi, mi -> failwith "Ambiguous"
```
## Pros and Cons

The advantages of making this adjustment to F# are:

Much easier handling of generic functions and generic operators in quotations involving types known only at runtime.

The disadvantages of making this adjustment to F# are:

Cost of implementation.
Another operator to learn.

## Extra information

Estimated cost (XS, S, M, L, XL, XXL): M/L

Related suggestions: (put links to related suggestions here)

## Affidavit (please submit!)

Please tick this by placing a cross in the box:
[x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue
[x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate
[x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:
[x] This is not a breaking change to the F# language design
[x] I or my company would be willing to help implement and/or test this

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.