Showing posts with label module. Show all posts
Showing posts with label module. Show all posts

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


Thursday, June 25, 2015

F# Modules

The question I want to answer in this post:
  • What's the difference between a module and a namespace?

Modules

Ok, let's start with the concept of a module. Here's the official Microsoft documentation on the topic: https://msdn.microsoft.com/en-us/library/dd233221.aspx. I found this particularly enlightening:
It [module] is implemented as a common language runtime (CLR) class that has only static members.
So think of it in the context of C# and it would look something like this:

class Blah 
{
    public static void Foo() { Console.WriteLine("Foo!"); }
}

That's a totally familiar concept, and it would look something like this (?) in F#:

module Blah

let Foo = fun () -> printfn "Foo!"

ASIDE: This example has brought up a new question, and something I stumbled over. Why doesn't this work?

let Foo = printfn "Foo!"

The reason is because this is actually setting Foo to the result of printfn "Foo!", which is val it : unit = (). So we need to make it clear that we're setting Foo to be a function, provided by the fun keyword, otherwise the compiler doesn't understand. This isn't a problem when you provide parameters, because the compiler can infer that you're creating a reference to a function.

UPDATE: Okay, as I've continued to learn I've gotten some more insight on this behavior. So the easier, possibly more "proper" way to do this is to use unit to represent the missing parameter:

let Foo () = printfn "Foo!"

This looks suspiciously like a function definition from a non-functional language, but it's not. And when you call the function, you've got to pass a unit as a parameter or it thinks you're just referencing the function:

// Not calling the function, just referring to it
> Foo;;
val it : (unit -> unit) = <fun:it@117-30>

// Function call taking a parameter of type unit!
> Foo ();; 
Foo!

val it : unit = ()

Ok, back to modules. There are apparently two kinds of module declarations: top level and "local":
  • Top level declarations mean that the whole file is in the module. The module declaration also needs to be the first declaration in the file.
  • Local modules means that you can have more than one module in a file. It's a little different than the top level module.
//Notice the identation!
module Blah1 =
    let Foo = fun () -> printfn "Foo!"

module Blah2=
    let Foo = fun () -> printfn "Foo2!"

I think that makes sense. Next rule:
  • If you don't use a top level module declaration, F# will insert one for you with the same name as the file, first letter uppercased. However, it will only do this if:
    • You're creating an exe and not a dll.
    • You've got a single file.
    • You're not including the file as a library.
  • So basically, if you're making a library you have to create a top level module or namespace.
I tripped over this rule when I tried to import a file in the REPL in a previous post. I had to go in and add a module declaration, remember?

Apparently you can also set an accessibility modifier, but I'm not going to mess with that right now.

Modules can also nest. So this is legal:

//Notice the identation!
module Blah1 =
    let Foo = fun () -> printfn "Foo!"

    module Blah2=
        let Foo = fun () -> printfn "Foo2!"

You should also notice that we've got the function Foo in Blah1 and again in Blah2. These are distinct Foos since they're in different modules and there's no name collision.

Alright, so I think I'm getting the hang of modules. Now...
  • What's the difference between a module and a namespace?
See Also:

F# Namespaces

Wednesday, June 24, 2015

F# Naming Conventions

One of the questions that I want to answer about namespaces and modules is this:
  • What are the proper naming conventions for F#?
I found a pretty authoritative-sounding answer here and here. To summarize:
  • PascalCase for most everything.
  • camelCase or PascalCase for let values and parameters.
That's simple enough!