-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Addition of InlineIfLambda
attribute in F# 6 opened the way for high-performance computation expressions (imagine one for zero allocation ZString builder). But we can do better. I propose we change translation rules to allow optimisations of for-loops in CE.
Consider following code using list.fs builder:
listc { for i = 0 to 10 do i*i }
According to F# spec compiler uses following rules to translate this expression:
T(for x = e1 to e2 do ce, V, C, q) = T(for x in e1 .. e2 do ce, V, C, q) (*)
T(for x in e do ce, V, C, q) = T(ce, V ⊕ {x}, λv.C(b.For(src(e), fun x -> v)), q) (**)
where e1 .. e2 (*)
is range operator from standard library that creates sequence of values and For (**)
is defined as:
member inline b.For(
sequence: seq<'TElement>,
[<InlineIfLambda>] body: 'TElement -> ListBuilderCode<'T>)
: ListBuilderCode<'T> =
b.Using (sequence.GetEnumerator(),
(fun e -> b.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke &sm))))
So simple for-loop allocates sequence and calls Using
method that might be undesirable in high-performance code. I propose to add new translation rule:
T(for x in e1 .. e2 do ce, V, C, q) = T(for x in range(e1, e2) do ce, V, C, q)
where range
denotes b.Range(e1, e2)
if builder b
contains Range
method. Otherwise, range(e1, e2)
denotes e1 .. e2
.
It allows to implement same optimisation compiler does for loops where
for x in 1..10 do
printfn $"{x}"
compiles into effective while
loop.
Let's draft it! First add Range
method:
member inline b.Range(e1: int, e1: int) = Range(Index(e1), Index(e2))
Then, add For
overload:
member inline b.For(
range: Range,
[<InlineIfLambda>] body: int -> ListBuilderCode<'T>) =
ListBuilderCode<_>(fun sm ->
for i = range.Start to range.End do
(body i).Invoke &sm)
The existing way of approaching this problem in F# is override range operator (..)
. Surprisingly CE uses operator from the context!
Pros and Cons
The advantage of making this adjustment to F# is allowing more high-performance scenarios for CE.
The disadvantages of making this adjustment to F# are increasing complexity of translations rules and compiler.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): L
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- I have searched both open and closed suggestions on this site and believe this is not a duplicate
- 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:
- This is not a breaking change to the F# language design
- I or my company would be willing to help implement and/or test this
For Readers
If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.