Showing posts with label REPL. Show all posts
Showing posts with label REPL. Show all posts

Saturday, June 27, 2015

Command Line Tools: Referencing DLL Interactively

Continuing with my investigation of the F# command line tools, I'm interested in exploring a couple of options: referencing a dll and including an F# script in the REPL. Let's start with a dll...

I'm going to use the FileDB dll from CodePlex. I honestly don't know anything about this project, but it looks like it should make for a good example. It's written in C#, and I guess I'll also stumble through calling C# from F#. Should theoretically be an easy thing.

First:

> #r "FileDB.dll";; 
--> Referenced '/Users/Matthew/Desktop/FileDB.dll' (file may be locked by F# Interactive process)

Okay, well that looks promising! By the way, I've got the REPL in the working directory with the FileDB.dll. Okay, so now let's see if I can get anything working. The internets tell me that open is the way to reference a .NET namespace in F#. 

> open Numeria.IO;;

Okay... Well, that gave me no output.  Let's see if I've got a class there someplace:

> FileDB;;

  FileDB;;
  ^^^^^^

/Users/Matthew/Desktop/stdin(3,1): error FS0803: Invalid use of a type name and/or object constructor. If necessary use 'new' and apply the constructor to its arguments, e.g. 'new Type(args)'. The required signature is:
FileDB(fileName: string, fileAccess: System.IO.FileAccess) : unit.

Whoa, interesting. So obviously there's something backing FileDB or I think I would get an error like this:

> BlahDB;;

  BlahDB
  ^^^^^^

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

So that's good! Let's try to create an empty db file. The FileDB documentation tells me that you would do this like so in C#:

var pathDB = @"C:\Temp\MyDB.fdb";

// Creating an empty FileDB archive
FileDB.CreateEmptyFile(pathDB);


So I'll try what I think is the right way in F#: 

> FileDB.CreateEmptyFile "Blah.db" ;;
val it : unit = ()

Worked!


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