How to create record in match pattern - f#

I want to write an application that read ip address from xml file. The file looks like
<range>
<start>192.168.40.1</start>
<end>192.168.50.255</end>
<subnet>255.255.255.0</subnet>
<gateway>192.168.50.1</gateway>
</range>
I create an records type to save the ip address
type Scope = { Start: IPAddress; End: IPAddress; Subnetmask: IPAddress; Gateway: IPAddress }
I wrote a unit function, that output the ip's.
loc
|> Seq.iter (fun e -> match e.Name.LocalName with
|"start" -> printfn "Start %s" e.Value
|"end" -> printfn "End %s" e.Value
|"subnet" -> printfn "Subnet %s" e.Value
|"gateway" -> printfn "Gateway %s" e.Value
| _ -> ())
How can I return the scope records type instead of unit?

As mentioned in the comments, the XML type provider makes this a lot easier. You can just point it at a sample file, it will infer the structur and let you read the file easily:
type RangeFile = XmlProvider<"sample.xml">
let range = RangeFile.Load("file-you-want-to-read.xml")
let scope =
{ Start = IPAddress.Parse(range.Start)
End = IPAddress.Parse(range.End)
Subnetmask = IPAddress.Parse(range.Subnet)
Gateway = IPAddress.Parse(range.Gateway) }
That said, you can certainly implement this yourself too. The code you wrote is a good start - there is a number of ways to do this, but in any case, you'll need to do some lookup based on the local name of the element (to find start, end, etc.).
One option is to load all the properties into a dictionary:
let lookup =
loc
|> Seq.map (fun e -> e.Name.LocalName, IPAddress.Parse(e.Value)
|> dict
Now you have a lookup table that contains IPAddress for each of the keys, so you can create Scope value using just:
let scope =
{ Start = lookup.["start"]; End = lookup.["end"];
Subnetmask = lookup.["subnet"]; Gateway = lookup.["gateway"] }
That said, the nice thing about the XML type provider is that it removes the need to do lookup based on string values and so you are less likely to make mistakes caused by typos.

Related

How do access a value that belongs to a record from that record itself?

I am REALLY new to F#, so I might have used the wrong terminology here. Please feel free to correct me if I am wrong, I would really appreciate it! Anyways, on to the question
I have a record that I have defined as so:
type EventSource = {
SourceName: string
Address: string
ParseDocument: HtmlDocument -> Event seq }
And I have created an instance of that record like so:
let lofSource = {
SourceName = "LOF"
Address = "https://lof.dk/syd/kurser"
ParseDocument = fun document ->
document.Descendants ["div"]
|> Seq.filter (fun d -> d.HasClass("item"))
|> Seq.map (
fun e ->
let linkElement
= e.Descendants (fun j -> j.HasClass "item-title")
|> Seq.head
|> (fun y -> y.Descendants ["a"])
|> Seq.map (fun fa -> fa.Attribute "href")
|> Seq.head
{
Title = e.AttributeValue "data-holdnavn"
Link = linkElement.Value()
Status = e.AttributeValue "data-status"
Image = Address //Here!
City = e.AttributeValue "data-bynavn"
Date = System.DateTime.ParseExact(e.AttributeValue("data-datosort"), "yyyyMMdd", null);
PostalCode = e.AttributeValue("data-postnr")})
}
On the line where I am trying to assign a value the Image member, It tells me that the value or constructor 'Address' is not defined.
I have tried using a self-identifier on the instantiation of the record and then trying to access Address like
this.Address
but it tells me that 'this' is not defined. I am guessing I am missing something quite fundamental here, can anyone help me? Is what I am trying to do nonsensical?
You can't do this with records. See: Reference a record from within itself during construction
You can do it with another binding (I couldn't get your code to compile and have simplified it):
type EventSource = {
SourceName: string
Address: string
ParseDocument: string -> string}
let lofSource =
let helloThere = "General Kenobi"
{
SourceName = "LOF"
Address = foo
ParseDocument = fun document ->
foo
}

Dynamic Lookup in F#

Can somebody help me with article of Tomas Petricek: http://tomasp.net/blog/fsharp-dynamic-lookup.aspx/#dynfslinks?
The problem is that it is severely outdated. I understand that namespaces
open Microsoft.FSharp.Quotations.Typed
open Microsoft.FSharp.Quotations.Raw
are gone. So I removed the openings. But there are still errors. "Typed" is not defined. "RecdGet" is not defined. And I suspect they are not the last. I'm trying to prove to my boss that F# is good to use for database normalization. Dynamic lookup of fields would really helped me to deal with similarly named fields having different prefixes.
There is also post of Tomas on fpish: https://fpish.net/topic/None/57493, which I understand predates the article
Here's a rough equivalent:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
type DynamicMember<'t,'u> = Expr<'t -> 'u>
let getValueReader (expr:DynamicMember<'recdT, 'fieldT>) =
// Match the quotation representing the symbol
match expr with
| Lambda(v, PropertyGet (Some (Var v'), pi, [])) when v = v' ->
// It represents reading of the F# record field..
// .. get a function that reads the record field using F# reflection
let rdr = Reflection.FSharpValue.PreComputeRecordFieldReader pi
// we're not adding any additional processing, so we just
// simply add type conversion to the correct types & return it
((box >> rdr >> unbox) : 'recdT -> 'fieldT)
| _ ->
// Quotation doesn't represent symbol - this is an error
failwith "Invalid expression - not reading record field!"
type SampleRec = { Str : string; Num : int }
let readStrField = getValueReader <# fun (r : SampleRec) -> r.Str #>
let readNumField = getValueReader <# fun (r : SampleRec) -> r.Num #>
let rc = { Str = "Hello world!"; Num = 42 }
let s, n = readStrField rc, readNumField rc
printfn "Extracted: %s, %d" s n

Computation expressions vs applicative functors and what not

Not entirely sure the title describes it ok, but I do have about the following code:
paket.dependencies:
source https://www.nuget.org/api/v2
nuget fsharpx.extras
nuget mongodb.driver
some.fsx:
#r #".\packages\MongoDB.Bson\lib\net45\MongoDB.Bson.dll"
#r #".\packages\MongoDB.Driver\lib\net45\MongoDB.Driver.dll"
#r #".\packages\MongoDB.Driver.Core\lib\net45\MongoDB.Driver.Core.dll"
#r #".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll"
open MongoDB
open MongoDB.Driver
open MongoDB.Bson
open MongoDB.Bson.Serialization
open FSharpx.Choice
let private createClient (connectString:string) = MongoClient(connectString)
let CreateClient = protect createClient
let private getDb name (client:IMongoClient) = client.GetDatabase(name)
let GetDB1 name client =
choose {
let! c = client
return! (protect (getDb name) c)
}
let GetDB2 name (client:Choice<IMongoClient, exn>) =
protect (getDb name)
<!> client
The point for this "excersise" was to write GetDB2 so that it does the same as GetDB1 but use operators (applicatives?), but I am at the moment not able to turn my head to manage this.
The above code compiles, but the signatures for
GetDB1 and GetDB2 are not equal, and Im obviously doing something not right.
val GetDB1 :
name:string ->
client:Choice<#MongoDB.Driver.IMongoClient,exn> ->
Choice<MongoDB.Driver.IMongoDatabase,exn>
val GetDB2 :
name:string ->
client:Choice<MongoDB.Driver.IMongoClient,exn> ->
Choice<Choice<MongoDB.Driver.IMongoDatabase,exn>,exn>
I have tried several versions and orders of doing things in GetDB2 but I more or less always end at same signature as above.
The general idea I initially had was to write small function doing the stuff they should and then add exception handling (protect) and then "wrap" and "unwrap" accordingly.
That might of course not be entirely right idea too.
Are someone able to point me in some directions here for further studies, code examples or anything? Any comments of any type are in fact welcome at this point ;-)
FSharpx doc
Addendum
I think the following should be about same as above, but without the mongodb dependencies.
#r #".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll"
type DataBase =
{
Name: string
}
type Client =
{
connectString: string
} with member this.GetDatabase name = {
Name = name
}
open FSharpx.Choice
let private createClient (connectString:string) = {
connectString= connectString
}
let CreateClient = protect createClient
let private getDb name (client:Client) = client.GetDatabase name
let GetDB1 name client =
choose {
let! c = client
return! (protect (getDb name) c)
}
let GetDB2 name client =
protect (getDb name)
<!> client
You are getting the compounding of types here because you have used the <!> operator, which is map. That is defined something like this:
let map f = function
| Choice1Of2 value = Choice1Of2 (f value)
| Choice2Of2 fail = Choice2Of2 fail
This has the signature ('T -> 'U) -> Choice<'T,'Failure> -> Choice<'U,'Failure>, i.e. the function f is used as a map inside the choice type. For example:
map (sprintf "%d")
has type Choice<int, 'Failure> -> Choice<string, 'Failure>. This is good for applying functions which do not use the Choice type - there is only one possible point of failure, and that happened before the call to map.
Your next function, however, produces a Choice type, but it takes a non-Choice type. This means that you want the errors to propagate through - if there is an error in the value, then choose that. If the value is fine, but there's an error in the function, then use that. If everything is successful, use that. This requires the two error types to be the same, which for you they are (exn).
This is describing the bind operation, defined like this:
let bind f = function
| Choice1Of2 value = f value
| Choice2Of2 fail = Choice2Of2 fail
with signature ('T -> Choice<'U,'Failure>) -> Choice<'T,'Failure> -> Choice<'U,'Failure>.
Note that bind is very similar to map, except that the latter raises the result into a Choice1Of2 - the mapped function is always successful.
In FSharpX, you can access bind by the |>-like operator >>=, or the <|-like operator <<=.
Finally, protect is a fancy way of catching a thrown exception into a Choice2Of2 exn. It is similar to map in that the passed function is of type 'T -> 'U, but the function can also throw an exception and the passed type is not a Choice. protect is defined something like this:
let protect f x =
try
Choice1Of2 (f x)
with
exn -> Choice2Of2 exn
so its signature is ('T -> 'U) -> 'T -> Choice<'U, exn>.
For more information on how everything is implemented, see the source of this computation expression.
Putting this all together, we can see why your example went wrong.
getDb name is a function Client -> DataBase
protect (getDb name) is a function Client -> Choice<DataBase, exn>
map (protect (getDb name)) is therefore a function Choice<Client, exn> -> Choice<Choice<DataBase, exn>, 'Failure>, because map works inside Choice.
What you want, though, is
let GetDB name client =
bind (protect (getDb name)) client
or in operator form,
let GetDB name client = client >>= protect (getDb name)
In general, if your mapping function has signature 'T -> 'U you want map. If it has 'T -> Choice<'U, 'Failure>, you want bind.

How do I invoke a function value that serves as a parameter on a function?

How do I invoke a function value that serves as a parameter on a function?
Specifically, my goal is to leverage a parameter of a function in which the parameter is actually a function.
In my case, I am trying to implement an interface for logging data.
Here's my code:
let logToFile (filePath:string) (message:string) =
let file = new System.IO.StreamWriter(filePath)
file.WriteLine(message)
file.Close()
let makeInitialDeposit deposit =
let balance = deposit |> insert []
sprintf "Deposited: %f" balance
let logDeposit deposit (log:'medium ->'data -> unit) =
deposit |> makeInitialDeposit
|> log
Note the following function:
let logDeposit deposit (log:'medium ->'data -> unit) =
deposit |> makeInitialDeposit
|> log
I get a compile error on the log function:
This construct causes code to be less generic than indicated by the
type annotations. The type variable 'medium has been constrained to be
type 'string'.
I understand that makeInitialDeposit returns string.
However, that string type is mapped to the generic type 'data.
Hence, a generic can be of any type right?
I then tried supplying the medium (i.e. file) argument:
let logDeposit deposit (log:'medium ->'data -> unit) medium =
deposit |> makeInitialDeposit
|> log medium
Then my error got updated to:
This construct causes code to be less generic than indicated by the
type annotations. The type variable 'data has been constrained to be
type 'string'.
My Goal
Ultimately, I just want to have an interface called log and pass in an implementation of that interface (i.e. logToFile).
Any guidance on how I should interpret the compile error based on my initial interpretation?
Insert function dependencies
let getBalance coins =
coins |> List.fold (fun acc d -> match d with
| Nickel -> acc + 0.05
| Dime -> acc + 0.10
| Quarter -> acc + 0.25
| OneDollarBill -> acc + 1.00
| FiveDollarBill -> acc + 5.00) 0.00
let insert balance coin =
coin::balance |> getBalance
The issue is that you have defined log :'medium ->'data -> unit.
You then take the result of deposit |> makeInitialDeposit, which has type string and pipe it into the log function. The compiler, logically, then infers that 'medium = string.
If you accept a 'medium argument in your logDeposit function then you simply move that inference along a step, deposit |> makeInitialDeposit is still a string so now 'data = string.
I think you are struggling though because these functions don't well model your domain and your logging logic is bleeding out into the rest of your code.
Why does makeInitialDeposit return a string?
Why does getBalance return a float but insert accepts a Coin list as its balance argument?
I would start by making a logging function that accepts three arguments:
let logToFile (filePath:string) (formatf : 'data -> string) data =
use file = new System.IO.StreamWriter(filePath)
file.WriteLine(formatf data)
data
It has type filePath : string -> (formatf : 'data -> string) -> (data : 'data) -> data. It accepts a path to log to, a function that formats something of type 'data as a string and some 'data to log to the file. Finally, it returns the data argument you supplied unchanged. That means you can, in principle, insert logging of any arbitrary value in your code anywhere.
I then set up some functions in the domain like this:
let valueOf = function
| Nickel -> 0.05m
| Dime -> 0.10m
| Quarter -> 0.25m
| OneDollarBill -> 1.00m
| FiveDollarBill -> 5.00m
let totalValue coins =
coins |> List.fold (fun acc coin -> acc + valueOf coin) 0.0m
let insert coins coin = coin::coins // returns Coin list
let makeInitialDeposit deposit = insert [] deposit // returns Coin list
I can then use these functions, inserting logging at any arbitrary point:
let balance =
makeInitialDeposit OneDollarBill
|> logToFile "file.txt" (sprintf "Initial Deposit: %A")
|> totalValue
|> logToFile "file2.txt" (sprintf "Balance : $%M")
This approach lets you fit logging around your domain rather than building your domain around logging.
First off, it seems odd that you're passing in a string like "Deposited: 0.25" as your path name. What you probably wanted is to have message as your first parameter and filePath as your second parameter for logToFile. And accordingly for log to be 'data -> 'medium -> unit.
Getting that out of the way, the issue for your compiler error is that makeInitialDeposit returns a string and when you then pipe that result into log the constraint happens. One way to get it to work is to add another parameter to logDeposit to converts string to 'data like this:
let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
deposit |> makeInitialDeposit
|> stringConverter
|> log
With this something like let dummyLog (a:int) (b:string) = () will work assuming you pass in the appropriate converter from string to int.

F# Read Fixed Width Text File

Hi I'm looking to find the best way to read in a fixed width text file using F#. The file will be plain text, from one to a couple of thousand lines long and around 1000 characters wide. Each line contains around 50 fields, each with varying lengths. My initial thoughts were to have something like the following
type MyRecord = {
Name : string
Address : string
Postcode : string
Tel : string
}
let format = [
(0,10)
(10,50)
(50,7)
(57,20)
]
and read each line one by one, assigning each field by the format tuple(where the first item is the start character and the second is the number of characters wide).
Any pointers would be appreciated.
The hardest part is probably to split a single line according to the column format. It can be done something like this:
let splitLine format (line : string) =
format |> List.map (fun (index, length) -> line.Substring(index, length))
This function has the type (int * int) list -> string -> string list. In other words, format is an (int * int) list. This corresponds exactly to your format list. The line argument is a string, and the function returns a string list.
You can map a list of lines like this:
let result = lines |> List.map (splitLine format)
You can also use Seq.map or Array.map, depending on how lines is defined. Such a result will be a string list list, and you can now map over such a list to produce a MyRecord list.
You can use File.ReadLines to get a lazily evaluated sequence of strings from a file.
Please note that the above is only an outline of a possible solution. I left out boundary checks, error handling, and such. The above code may contain off-by-one errors.
Here's a solution with a focus on custom validation and error handling for each field. This might be overkill for a data file consisting of just numeric data!
First, for these kinds of things, I like to use the parser in Microsoft.VisualBasic.dll as it's already available without using NuGet.
For each row, we can return the array of fields, and the line number (for error reporting)
#r "Microsoft.VisualBasic.dll"
// for each row, return the line number and the fields
let parserReadAllFields fieldWidths textReader =
let parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(reader=textReader)
parser.SetFieldWidths fieldWidths
parser.TextFieldType <- Microsoft.VisualBasic.FileIO.FieldType.FixedWidth
seq {while not parser.EndOfData do
yield parser.LineNumber,parser.ReadFields() }
Next, we need a little error handling library (see http://fsharpforfunandprofit.com/rop/ for more)
type Result<'a> =
| Success of 'a
| Failure of string list
module Result =
let succeedR x =
Success x
let failR err =
Failure [err]
let mapR f xR =
match xR with
| Success a -> Success (f a)
| Failure errs -> Failure errs
let applyR fR xR =
match fR,xR with
| Success f,Success x -> Success (f x)
| Failure errs,Success _ -> Failure errs
| Success _,Failure errs -> Failure errs
| Failure errs1, Failure errs2 -> Failure (errs1 # errs2)
Then define your domain model. In this case, it is the record type with a field for each field in the file.
type MyRecord =
{id:int; name:string; description:string}
And then you can define your domain-specific parsing code. For each field I have created a validation function (validateId, validateName, etc).
Fields that don't need validation can pass through the raw data (validateDescription).
In fieldsToRecord the various fields are combined using applicative style (<!> and <*>).
For more on this, see http://fsharpforfunandprofit.com/posts/elevated-world-3/#validation.
Finally, readRecords maps each input row to the a record Result and chooses the successful ones only. The failed ones are written to a log in handleResult.
module MyFileParser =
open Result
let createRecord id name description =
{id=id; name=name; description=description}
let validateId (lineNo:int64) (fields:string[]) =
let rawId = fields.[0]
match System.Int32.TryParse(rawId) with
| true, id -> succeedR id
| false, _ -> failR (sprintf "[%i] Can't parse id '%s'" lineNo rawId)
let validateName (lineNo:int64) (fields:string[]) =
let rawName = fields.[1]
if System.String.IsNullOrWhiteSpace rawName then
failR (sprintf "[%i] Name cannot be blank" lineNo )
else
succeedR rawName
let validateDescription (lineNo:int64) (fields:string[]) =
let rawDescription = fields.[2]
succeedR rawDescription // no validation
let fieldsToRecord (lineNo,fields) =
let (<!>) = mapR
let (<*>) = applyR
let validatedId = validateId lineNo fields
let validatedName = validateName lineNo fields
let validatedDescription = validateDescription lineNo fields
createRecord <!> validatedId <*> validatedName <*> validatedDescription
/// print any errors and only return good results
let handleResult result =
match result with
| Success record -> Some record
| Failure errs -> printfn "ERRORS %A" errs; None
/// return a sequence of records
let readRecords parserOutput =
parserOutput
|> Seq.map fieldsToRecord
|> Seq.choose handleResult
Here's an example of the parsing in practice:
// Set up some sample text
let text = """01name1description1
02name2description2
xxname3badid-------
yy badidandname
"""
// create a low-level parser
let textReader = new System.IO.StringReader(text)
let fieldWidths = [| 2; 5; 11 |]
let parserOutput = parserReadAllFields fieldWidths textReader
// convert to records in my domain
let records =
parserOutput
|> MyFileParser.readRecords
|> Seq.iter (printfn "RECORD %A") // print each record
The output will look like:
RECORD {id = 1;
name = "name1";
description = "description";}
RECORD {id = 2;
name = "name2";
description = "description";}
ERRORS ["[3] Can't parse id 'xx'"]
ERRORS ["[4] Can't parse id 'yy'"; "[4] Name cannot be blank"]
By no means is this the most efficient way to parse a file (I think there are some CSV parsing libraries available on NuGet that can do validation while parsing) but it does show how you can have complete control over validation and error handling if you need it.
A record of 50 fields is a bit unwieldy, therefore alternate approaches which allow dynamic generation of the data structure may be preferable (eg. System.Data.DataRow).
If it has to be a record anyway, you could spare at least the manual assignment to each record field and populate it with the help of Reflection instead. This trick relies on the field order as they are defined. I am assuming that every column of fixed width represents a record field, so that start indices are implied.
open Microsoft.FSharp.Reflection
type MyRecord = {
Name : string
Address : string
City : string
Postcode : string
Tel : string } with
static member CreateFromFixedWidth format (line : string) =
let fields =
format
|> List.fold (fun (index, acc) length ->
let str = line.[index .. index + length - 1].Trim()
index + length, box str :: acc )
(0, [])
|> snd
|> List.rev
|> List.toArray
FSharpValue.MakeRecord(
typeof<MyRecord>,
fields ) :?> MyRecord
Example data:
"Postman Pat " +
"Farringdon Road " +
"London " +
"EC1A 1BB" +
"+44 20 7946 0813"
|> MyRecord.CreateFromFixedWidth [16; 16; 16; 8; 16]
// val it : MyRecord = {Name = "Postman Pat";
// Address = "Farringdon Road";
// City = "London";
// Postcode = "EC1A 1BB";
// Tel = "+44 20 7946 0813";}

Resources