tags:

views:

84

answers:

2

the (..) and (.. ..) operators in F# are unrolled at some point, is that a compile time operation or a run time operation?

in either case, what are the performance of this? i.e. is it possible to build a custom function that does those operations faster?

+2  A: 

Run time. F# will only very rarely run your code as part of compilation - the only case I can think of is code in NumericLiteralX modules. Besides, in code like this:

let f n = [1 .. n]

the upper bound isn't even known at compile time. Of course it's possible that as an implementation detail the F# compiler explicitly unrolls definitions where both bounds are statically known values of a known type (such as int), but semantically it should always be the same as if it's done at run time.

Regarding your second question, faster than what?

kvb
+2  A: 

I think that the reply from kvb answers most of the concerns. However, I think that a more precise answer is that ranges are evaluated lazily at runtime. Here are some more details how ranges work...

When you use for example 1 .. 10 somewhere in your code, it is simply translated to some method call. The call depends on the context and numeric types used.

  • For [ 1 .. 10 ] or other sequence expressions and for loop, the compiler will generate something like RangeInt32(1, 1, 10) (the additional parameter is the step).

  • When you have something like obj.[ 1 .. ] and obj is some object that supports slicing (e.g. matrix type), then it will be translated to obj.GetSlice(Some(1), None) (note that in this case the upper/lower bound may be missing).

Now it is quite easy to answer your question - it is a method call that will be evaluated at runtime. However it is important to note that the whole range may not need to be evaluated! For example:

let nums = seq { 1 .. 10 } |> Seq.take 1

The sequence expression will be translated to a call to RangeInt32. This will just return a value of type seq<int> which is evaluated lazily. The call to take 1 takes only first element, so only the first number from the range will be needed and evaluated.

I don't think your own implementation of ranges could be any different than the standard one, however you can provide your implementation as a member of an object. Then you could write myObj.[1 .. 10] (and the result could be any type you want). To do that, you'll need an instance method GetSlice, which is in more detail discussed here.

Tomas Petricek