Showing posts with label cli-tools. Show all posts
Showing posts with label cli-tools. Show all posts

Sunday, June 28, 2015

Command Line Tools

Okay, so I'm going to start by inspecting the command line tools for F#. This depends on whether you're using .NET or mono. For mono:

fsharpc == command line compiler
fsharpi == command line interactive (REPL)

For .NET:

fsc.exe == command line compiler
fsi.exe == command line interactive (REPL)

So I'll start by messing around with the interpreter. A REPL is a hugely beneficial tool when learning a language or trying out code you don't understand. It won't take me too far, because doing anything complicated requires more flexibility, but it's a great way to test understanding. 

I'm also going to use the tutorials over at Try F#. You can do the same if you like. Okay, I'm ready to learn some basics!

Matthews-MacBook-Pro:~ Matthew$ fsharpi

The REPL tells me I can get help with the command #help;; so I'll try that first. Here are a few fundamentals of the REPL:
  • Every statement or block needs to be followed with ;;. This tells the REPL to process the command. This doesn't seem to be part of the language,  just a convention of the interactive F#.
  • The REPL responds to commands that start with a hash mark. It took me a minute to figure this out, because I didn't use #help;;. Be smarter than me and use the help menu!
    • #help;;  Access the help menu. 
    • #quit;;  Quit the REPL.
    • #r "File.dll";;  
      • Assuming that the "r" stands for reference; allows you to reference a dll. I'll take that for a spin.
    • #I "path";;  
      • Looks like you can add a path to include dlls. 
    • #load "file.fs" ...;; 
      • If I want to load a script and then play around with it in the REPL, this command should do that for me.
    • #time ["on"|"off"];; 
      • No idea.  Maybe this gives you stopwatch data about the executing code?
I'm curious about that #time;; option so I'll try that first. 

> #time "on";;

--> Timing now on

> let me = "spotaneous";;
Real: 00:00:00.000, CPU: 00:00:00.003, GC gen0: 0, gen1: 0

val me : string = "spotaneous"

That's pretty cool! So I can do some primitive optimization in the REPL by comparing the timing of different code options. It's not something I need right away, but cool nonetheless.

It's worth noting that this is how the REPL reports the value of a reference:

val me : string = "spotaneous"

It looks a little confusing to me, since it looks suspiciously like a variable assignment in another language, but it's not. I can check the value referenced by:

> me;;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0
val it : string = "spotaneous"

Friday, June 26, 2015

Command Line Tools: Loading a Script Interactively

Last post, I loaded a dll in the interactive shell (REPL) and called a method. This time, I want to explore a similar option: loading code from an external script. I've got a script adapted from the examples on http://www.tryfsharp.org/ and I'll use that for my example.

The questions I'd like to answer for myself:

  • How do I load an external F# script and call methods on it in the REPL?

I've got this saved in fsharp.fs:

// This code is saved in fsharp.fs
// "Date,Open,High,Low,Close,Volume,Adj Close"
let stockData =
    [ 
      "2012-03-30,32.40,32.41,32.04,32.26,31749400,32.26";
      "2012-03-29,32.06,32.19,31.81,32.12,37038500,32.12";
      "2012-03-28,32.52,32.70,32.04,32.19,41344800,32.19";
      "2012-03-27,32.65,32.70,32.40,32.52,36274900,32.52";
      "2012-03-26,32.19,32.61,32.15,32.59,36758300,32.59";
      "2012-03-23,32.10,32.11,31.72,32.01,35912200,32.01";
      "2012-03-22,31.81,32.09,31.79,32.00,31749500,32.00";
      "2012-03-21,31.96,32.15,31.82,31.91,37928600,31.91";
      "2012-03-20,32.10,32.15,31.74,31.99,41566800,31.99";
      "2012-03-19,32.54,32.61,32.15,32.20,44789200,32.20";
      "2012-03-16,32.91,32.95,32.50,32.60,65626400,32.60";
      "2012-03-15,32.79,32.94,32.58,32.85,49068300,32.85";
      "2012-03-14,32.53,32.88,32.49,32.77,41986900,32.77";
      "2012-03-13,32.24,32.69,32.15,32.67,48951700,32.67";
      "2012-03-12,31.97,32.20,31.82,32.04,34073600,32.04";
      "2012-03-09,32.10,32.16,31.92,31.99,34628400,31.99";
      "2012-03-08,32.04,32.21,31.90,32.01,36747400,32.01";
      "2012-03-07,31.67,31.92,31.53,31.84,34340400,31.84";
      "2012-03-06,31.54,31.98,31.49,31.56,51932900,31.56";
      "2012-03-05,32.01,32.05,31.62,31.80,45240000,31.80";
      "2012-03-02,32.31,32.44,32.00,32.08,47314200,32.08";
      "2012-03-01,31.93,32.39,31.85,32.29,77344100,32.29";
      "2012-02-29,31.89,32.00,31.61,31.74,59323600,31.74"; ]

let splitCommas (x:string) =
    x.Split([|','|])

let processStockData sData =
    sData
    |> List.map splitCommas
    |> List.maxBy (fun x -> abs(float x.[1] - float x.[4]))
    |> (fun x -> x.[0])

Now, in theory, I should be able to load this script and call either of the functions that have been defined here. I haven't done this before, but let's give this a try. The help menu says this:

#load "file.fs" ...;; Load the given file(s) as if compiled and referenced


Okay, so in theory I should be able to do this:

> #load "fsharp.fs";; 
[Loading /Users/Matthew/Desktop/sharp.fs]

/Users/Matthew/Desktop/sharp.fs(2,1): error FS0222: Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. Only the last source file of an application may omit such a declaration.

Oops. Well, that didn't quite work. The internets tell me slightly more than this verbose error message. I'm going to add a module statement at the top of the fsharp.fs file:

module Fsharp

And now let's try it again:


> #load "fsharp.fs";;
[Loading /Users/Matthew/Desktop/sharp.fs]

namespace FSI_0006
  val stockData : string list
  val splitCommas : x:string -> string []

  val processStockData : sData:string list -> string

Looks like that worked. It's worth noting that at this point I really have only the vaguest notion of what either module or namespace mean in the context of F#. Of course, my experience with other languages give me some pre-conceived notions but in the case of F# I'm just taking this on faith. I think this is an important concept in learning new technology: don't let not knowing get you stuck. The distinction between these ideas will become clear in time. For now, I'm focused on getting the REPL to include an external F# script...

Now the moment of truth:

> processStockData stockData;;

  processStockData stockData;;
  ^^^^^^^^^^^^^^^^

/Users/Matthew/Desktop/stdin(13,1): error FS0039: The value or constructor 'processStockData' is not defined

Oof, not defined. Well, it said when I loaded the script that processStockData was defined, but I'm guessing it's nested inside of the namespace listed above. Okay, so I've got to figure that out. I've got a hunch:

/Users/Matthew/Desktop/stdin(14,1): error FS0039: The namespace or module 'Fsharp' is not defined
> Fsharp.processStockData stockData;;

  Fsharp.processStockData stockData;;
  -----------------------^^^^^^^^^

/Users/Matthew/Desktop/stdin(15,24): error FS0039: The value or constructor 'stockData' is not defined

Hey, look at that: Fsharp is the name I gave the module, and I used that to prefix the method and now it's complaining about missing stockData. So it stands to reason that I should be able to prefix that as well:

> Fsharp.processStockData Fsharp.stockData;;
val it : string = "2012-03-13"

Boom! You can work through the original tutorial yourself but I can tell you that "2012-03-13" is the expected output.

So this session answered a few questions but raised a few others:
  • What's the difference between a module and a namespace?
  • How do you import a namespace into the current namespace?
  • What are the best practices for handling modules vs. namespaces? Naming conventions?
GOTO