Tuesday, January 15, 2008
I'm starting a brand new series of short articles about F#. The plan is to describe features that, for me, make F# a compelling and enjoyable .NET language. So far, I have 10-15 articles in mind, but I'm open to suggestions. If you have any ideas for additional topics, please email them to dustin AT diditwith.net.

The Interactive Environment

Like Python, Ruby and many other programming languages, F# provides an interactive scripting environment. However, F# is different in that the interactive environment is not an interpreter. Instead, it dynamically compiles code on-the-fly.

There are two ways to load this environment:

  • Run fsi.exe from the bin subdirectory of the F# distribution.
  • Load the F# Interactive for Visual Studio add-in from the Visual Studio Add-in Manager.

Once the environment is loaded, a splash screen is displayed. (NOTE: the examples here use fsi.exe.)

MSR F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.3.7, compiling for .NET Framework Version v2.0.50727

NOTE:
NOTE: See 'fsi --help' for flags
NOTE:
NOTE: Commands: #r <string>;;    reference (dynamically load) the given DLL.
NOTE:           #I <string>;;    add the given search path for referenced DLLs.

NOTE:           #use <string>;;  accept input from the given file.
NOTE:           #load <string> ...<string>;;
NOTE:                            load the given file(s) as a compilation unit.
NOTE:           #time;;          toggle timing on/off.
NOTE:           #types;;         toggle display of types on/off.
NOTE:           #quit;;          exit.
NOTE:
NOTE: Visit the F# website at http://research.microsoft.com/fsharp.
NOTE: Bug reports to fsbugs@microsoft.com. Enjoy!

>

At this point, it's easy to start typing F# code. To execute code, type a double semi-colon. The following bit of code, when typed into the interactive environment, will instantiate and display a new .NET Windows Form:

> open System.Drawing
- open System.Windows.Forms;;

> let myForm = new Form(Text = "Hello, World!", Visible = true);;

val myForm : Form

The first two lines open the System.Drawing and System.Windows.Forms namespaces. This is analogous to C#'s using and VB's Imports statements. It isn't necessary to reference the System.Drawing.dll or System.Windows.Forms.dll assemblies because they are implicitly referenced by the environment.

The third line instantiates a new Form, sets its Text and Visible properties, and binds it to the name myForm. Because the code is dynamically compiled and executed, the form is displayed immediately.

Hello, World! Form

Now that the form is instantiated, it can be manipulated at runtime.

> myForm.BackColor <- Color.Blue;;
val it : unit = ()

When executed, the above code changes the form like so:

Hello, World! Form (colored)

The F# Interactive Environment is a great way to break out of the standard edit-compile-debug rut and prototype some code. It can even output to a .NET assembly. Run "fsi.exe --help" to see more ways in which the interactive environment can be used.

posted on Tuesday, January 15, 2008 8:06:10 AM (Pacific Standard Time, UTC-08:00)  #    Comments [6]

kick it on DotNetKicks.com
Tuesday, January 15, 2008 10:20:09 AM (Pacific Standard Time, UTC-08:00)
It's great to see languages with interactive consoles popping up on .NET (and the JVM as well). The console is one of those things that you can never appreciate until you sit down and actually try it. Escaping the "edit-compile-run" cycle is quite liberating; there are insights you could never get from the slow feedback of a separate compilation step. Thanks for posting about this!

However, I do have a small nitpick: Python is no more "interpreted" than .NET is. When you enter code at Python's console, it compiles it to byte code and executes it within a VM, which is the same process going on with F#. This has been covered on the Python list. Note that this is not true for current versions of Ruby - it parses the source code into a tree and traverses the tree to execute it. This is probably what you mean by "interpreted", and also explains why Ruby is somewhat slower than Python. There are VM based implementations of Ruby in the works, however. YARV and Rubinius, for example.
Tuesday, January 15, 2008 10:38:15 AM (Pacific Standard Time, UTC-08:00)
Gary, thanks for the nitpick. I really appreciate your post. My understanding of interpretation may be a little loose. It's my understanding that the byte code is still "interpreted" by the Python runtime, correct? In other words, it isn't JIT-compiled to native code by the runtime and executed on the CPU like F# or any other .NET language?
Tuesday, January 15, 2008 12:23:50 PM (Pacific Standard Time, UTC-08:00)
Right, CPython's byte code is never JITed into machine code (although that clearly depends on the implementation, and may not be true for Jython or IronPython). So if we define "interpreted" to mean "not executing as native code", then CPython is definitely interpreted. I'm just not sure about how useful that distinction is. Here are some examples of what I mean.

1. If I add partial JIT to CPython's VM, is it suddenly not "interpreted" any more? It's the same byte code emitted by the same compiler, after all.

2. What about Java? Unlike .NET, it doesn't JIT everything before running it. Whether my code is "interpreted" could change on a millisecond-by-millisecond basis!

3. Even our "native" executables aren't truly native to the CPU's execution core. Modern CPUs are actually VMs of a sort - they translate x86 instructions into a RISC-like "microarchitecture" and execute that directly.

Maybe we just need a new word to distinguish "compiled to something" from "run straight from a parse tree". From a performance perspective, that's a much more important distinction. There have been slow VMs and fast VMs (e.g. the JVM); there have been fast hardware architectures and slow ones (e.g. the Crusoe processors); but there will probably never be an implementation of a tree-crawling interpreter that can beat even a mediocre VM. So I think by lumping VMs and tree-crawlers together as "interpreted", we lose sight of what's fundamentally slow and what's just not optimized.

Sorry, I've gone on way too much. I find this stuff very interesting. :)
Tuesday, January 15, 2008 12:48:19 PM (Pacific Standard Time, UTC-08:00)
> Sorry, I've gone on way too much. I find this stuff very interesting. :)

Not at all Gary -- I enjoy this stuff as well. ;-) I agree with you that lumping things together as interpreted vs. compiled is really not very fair. Perhaps the definition I've been using for awhile is something closer to "interpretation occurs when execution does not happen on the processor." In other words, when a VM executes code itself (byte or otherwise), there's a clear interpretation step. That was certainly the distinction that I was going for since the runtime execution of F# is very different than that of CPython.

Part of the confusion here is that there are two levels of interpretation in an interactive environment: 1) how the environment deals with the source and 2) How it is executes. At the first level, both Python and F# are clearly compiled. The CPython becomes byte code and the F# becomes IL. However, at the second level, the CPython byte code is interpreted whereas the F# IL is JITted and run directly on the processor. So, there's a difference -- a pretty big difference performance-wise.

Of course, this all goes out the window if you read "Python" as "IronPython" and "Ruby" as "IronRuby." :-) In that case, the code is executed in the same way as F#. However, the F# will still prove to be faster due to the overhead of the DLR that IronPython and IronRuby have.

Anyway, an important difference between the F# interactive environment and *many* others is that code is JITted to native instructions and run directly on the processor. Whew! :-D
Wednesday, January 16, 2008 12:59:49 PM (Pacific Standard Time, UTC-08:00)
I totally agree, except maybe for some minor differences of opinion with terminology, as in my original post. But those differences mostly stem from my attempts to avoid equating "dynamic" with "slow". Treating "compiled" and "interpreted" as opposites ignores the fact that some "interpreted" languages are compiled, but the target just isn't a physical architecture.

The unfortunate thing about all this is that dynamic languages generally live in a VM (or even worse, a tree crawling interpreter), and static languages generally either get JITed or compiled straight to native code. That's great for the static languages, but the dynamic ones are getting shafted, and it's their own fault. As usual, Lisp is the exception: there have been many Lisp implementations with native compilers, and some of them were quite fast. But the general trend of dynamic languages on VMs leads people to assume that dynamic languages must be slow, which isn't the case.

Fortunately, it seems like interest in dynamic language implementations is picking up. There's pypy, Rubinius, YARV, PUGS, Parrot, etc. None of these is production quality yet, but when we finally get a fast, JITed implementation of a mainstream dynamic language, it's really going to wake people up. Or so my wishful thinking goes!

We really should've had an open space session about this at CodeMash. Maybe next year. ;)
Thursday, February 07, 2008 7:17:53 AM (Pacific Standard Time, UTC-08:00)
Well the F# seems very interesting Language, & i m getting more and more hooked to it..

But the above talk at interpreter, dynamic, Jited or Compiled languages seemed very knowledgeful...

Thx for all the info..
Comments are closed.