0

For a while F# has supported the ability to auto-quote using [<ReflectedDefinitionAttribute>]. Is there anything similar for laziness?

e.g.

    member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) = ... 
    member __.Thunked ([<LazyAttribute>] thunk:Lazy<'T>) = ... 

I suppose I could use something like

    member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) = 
        Lazy (evaluate (<@ fun () -> %quotation @>)) // evaluate using Unquote or similar

But wouldn't this be costly?

UPDATE:

I found a hack, it's not exactly what I would like but it give the correct behavior.

type Signal = Signal with
    member __.Return x = x
    member __.Delay (f:unit -> _) = f

let a = Signal { return randint }
let b = Signal { return randint }
let c = Signal { return a() + b() }
mazin
  • 395
  • 2
  • 7

1 Answers1

3

There is nothing like the ReflectedDefinition attribute for automatically turning things into delayed Lazy<'T> computations.

You are right that automatically quoting the argument achieves something like this. You could use the (very limited) LeafExpressionConverter.EvaluateQuotation to do this for some limited kinds of expressions, but as you note, this would be inefficient. The following is a proof of concept though (but you cannot call custom functions in the branches as this uses LINQ expressions):

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers

type A = 
  static member If<'T>(c:bool,
      [<ReflectedDefinition>] t:Expr<'T>,
      [<ReflectedDefinition>] f:Expr<'T>) = 
    if c then LeafExpressionConverter.EvaluateQuotation t :?> 'T
    else LeafExpressionConverter.EvaluateQuotation f :?> 'T

A.If(1 = 2, 0, 1)

In practice, I think a more reasonable approach is to just use the built-in Lazy<'T> values. F# has a (not widely known) lazy keyword that gives you a bit nicer syntax for creating those:

let iff c (t:Lazy<_>) (f:Lazy<_>) = 
  if c then t.Value else f.Value

iff (1 = 2) 
  (lazy (printfn "true"; 41)) 
  (lazy (printfn "false"; 42))
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • I made a feature request https://github.com/fsharp/fslang-suggestions/issues/1003. I've found call-by-name to be more than useful in Scala. – mazin May 04 '21 at 10:53
  • @AdamNathan It would be really interesting to see what are the most useful uses cases for this in Scala - I can imagine some, but would be interested in seeing how this gets used. Perhaps you could add them to the feature request? – Tomas Petricek May 04 '21 at 20:00
  • Judging from your bio you probably know the pros/cons and the whens and whys more than me. As you suggest, it's just syntactic sugar, but aren't auto-quotes also? I believe it offers a nice middle ground between heavy-weight quotations and over-eagerness; also in some cases it could fill the role of basic macros. I updated the suggestion, I would love your feedback. – mazin May 06 '21 at 12:23
  • @AdamNathan I may know a few things about F#, but I have no idea about the use cases for this in Scala and knowing the most common specific use cases for this in Scala would be very interesting. (It is similar to auto-quoting - that is mainly useful for things like translating F# queries to SQL and possibly being able to automatically infer labels when you write e.g. `data |> plot(fun x -> x.Whatever)`. So I'm interested in similarly specific use cases for call-by-name... – Tomas Petricek May 06 '21 at 21:13
  • I wrote some useful examples on the f# suggestion. It is mostly for efficiency. One classic example, aside from the one you gave, is efficient logging. If I have lot's of `Log.Info msg`, the `msg` can be evaluated on demand only on certain environments. In my case it is the opposite; I am working on a signal library https://github.com/adam-nathan/Signalz, which is essentially a mutable thunked expression, so I can force partial re-evaluation. I actually think I found a hack. I will post soon. – mazin May 10 '21 at 11:19
  • @AdamNathan Thank you! This is very useful. – Tomas Petricek May 10 '21 at 21:43