I have a application which runs as a server, dose some calculations and returns a value. I have created a discriminated union type of MessageType so I can have different types of messages passed between applications.
The MessageType is made up of an ExchangeMessage of type ExchangeFrame. The question I have is how to access the values of ExchangeFrame from the MessageType.
The code might explain it better
[<CLIMutable>]
type ExchangeFrame =
{
FrameType: FrameType
Amount: double;
ConvertTo: Currency
ConvertFrom: Currency
}
type MessageType = ExchangeMessage of ExchangeFrame
let server () =
use context = new Context()
// socket to talk to clients
use responder = context |> Context.rep
"tcp://*:5555" |> Socket.bind responder
Console.WriteLine("Server Running")
while true do
// wait for next request from client
let messageReceived = responder |> Socket.recv |> decode |> deserializeJson<MessageType>
//Do Calculations
let total = doCalculations //MessageReceived.ExchangeMessage.Amount 3.0
// send reply back to client
let message = encode <| total
message |> Socket.send responder
server ()
As the design stands, you can access the exchange frame by (1) pattern matching to extract the frame from the MessageType, and then (2) dotting into the frame to extract a field, like this:
let msgType = // creation
let (ExchangeMessage frame) = msgType
let amount = frame.Amount
But see my comments to the question.
Related
I have just started using F# and my brain is broken trying to figure out how to work with its types without having to resort to an OO type of programming.
Here is my situation I basically want to create a method where I provide the type and the Id and it returns to me the object on the database.
So basically this is what I get so far.
let client = MongoClient()
let database = client.GetDatabase("testdb")
let lowerCase (str : string) =
str.ToLower()
let nameOf (classType: Type) =
classType.Name
let nameTypeOf<'a> =
nameOf typeof<'a>
let getCollection<'a> =
let collectionName = nameTypeOf<'a> |> lowerCase
database.GetCollection<'a> collectionName
let dbSelect<'a> id =
let collection = getCollection<'a>
collection.Find(fun(x) -> x.Id = id).First()
So my problem is with the dbSelect, obviously it does not compile since x is generic, basically I wanted to create an interface with the Id and all my objects interface with it.
I do know how to do it using classes and inheritances, but I am avoiding having to use instanced classes outside interop with c# libraries. What would be the best functional way to do it, if there is any.
This is what I was eexpecting to call it with
type IDbObject =
abstract Id: string
type Item =
{
Id: string
Name: string
}
interface IDbObject with
member x.Id = x.Id
let item =
selectDb<Item> "5993592a35ce962b80da1e22"
Any help would be appreciated.
And if anyone want to point out how crappy my code is, any feedback is really appreciated
I don't think the solution here is much different from what you'd have in C#. You can constrain the generic type to use the interface members, getting something roughly like this:
let getCollection<'a when 'a :> IDbObject> () =
let collectionName = nameTypeOf<'a> |> lowerCase
database.GetCollection<'a> collectionName
let dbSelect<'a when 'a :> IDbObject> id =
let collection = getCollection<'a>()
collection.Find(fun (x : 'a) -> x.Id = id).First()
The type of dbSelect should be inferred to be string -> #IDbObject, and be coerced to string -> 'a at the call site.
I'm having a problem getting my DU working as expected. I've defined a new DU which either has a result of type <'a> or any Exception derived from System.Exception
open System
// New exceptions.
type MyException(msg : string) = inherit Exception(msg)
type MyOtherException(msg : string) = inherit MyException(msg)
// DU to store result or an exception.
type TryResult<'a, 't> =
| Result of 'a
| Error of 't :> Exception
//This is fine.
let result = Result "Test"
// This works, doing it in 2 steps
let ex = new MyOtherException("Some Error")
let result2 = Error ex
// This doesn't work. Gives "Value Restriction" error.
let result3 = Error (new MyOtherException("Some Error"))
I can't understand why it is allowing me to create an "Error" if I do it in 2 steps, but when i'm doing the same thing on a single line, I get a Value Restriction error.
What am i missing?
Thanks
UPDATE
Looking at the post by #kvb, adding type information each time I need to create an Error seemed a bit verbose, so I wrapped it up into an additional method which creates an Error and is a bit more succinct.
// New function to return a Result
let asResult res : TryResult<_,Exception> = Result res
// New function to return an Error
let asError (err : Exception) : TryResult<unit,_> = Error(err)
// This works (as before)
let myResult = Result 100
// This also is fine..
let myResult2 = asResult 100
// Using 'asError' now works and doesn't require any explicit type information here.
let myError = asError (new MyException("Some Error"))
I'm not sure if specifying an Error with 'unit' will have any consequences I haven't foreseen yet.
TryResult<unit,_> = Error(err)
Consider this slight variation:
type MyOtherException(msg : string) =
inherit MyException(msg)
do printfn "%s" msg
let ex = new MyOtherException("Some Error") // clearly, side effect occurs here
let result2 = Error ex // no side effect here, but generalized value
let intResults = [Result 1; result2]
let stringResults = [Result "one"; result2] // can use result2 at either type, since it's a generalized value
let result3 = Error (MyOtherException("Some Error")) // result would be of type TryResult<'a, MyOtherException> for any 'a
// In some other module in a different compilation unit
let intResults2 = [Result 1; result3] // why would side effect happen here? just using a generic value...
let stringResults2 = [Result "one"; result3] // likewise here...
The issue is that it looks like result3 is a value, but the .NET type system doesn't support generic values, it only supports values of concrete types. Therefore, the MyOtherException constructor needs to be called each time result3 is used; however, this would result in any side effects occurring more than once, which would be surprising. As Ringil suggests, you can work around this by telling the compiler to treat the expression as a value anyway:
[<GeneralizableValue>]
let result3<'a> : TryResult<'a,_> = Error(new MyOtherException("Some Error"))
This is fine as long as the constructor doesn't have side effects.
You can do:
let result3<'a> = Error (new MyOtherException("Some Error"))
EDIT:
As for why you can't do it in one step, first note that this results in the same error:
let result4 = Result (new MyOtherException("Some Error"))
As does this:
let result4 = Result ([|1;|])
But that this works:
let result4 = Result ([1;])
What's similar about Exception and Arrays, but not Lists? It's their mutability. The value restriction will bother you when you try to do make a TryResult with a type that is mutable in a single step.
Now as for why the two step process solves this, it's because the constructor make the whole function not generalizable because you're applying a function to the constructor. But splitting it into two steps solves that. It is similar to Case 2 here on MSDN.
You can read more about it at the above MSDN article and the why this happens in this more indepth blog post.
Reactive.Linq's GroupBy leaves you with an IObservable<IGroupedObservable<'TKey, 'TValue>>. How do you get the values from the IGroupedObservable? The key is accessible by x.Key, so I suppose the values could be projected by some transformation of sorts.
This is roughly what I want to do:
open System.Reactive.Linq
let doStuffWithEvenNumbers i = i*10
let doStuffWithOddNumbers i = i*3
let numbers = Observable.Range(0, 10)
let groups = numbers.GroupBy(fun i -> i % 2 = 0)
let subscription1 = groups.Where(fun i -> i.Key).Subscribe(doStuffWithEvenNumbers)
let subscription2 = groups.Where(fun i -> not i.Key).Subscribe(doStuffWithOddNumbers)
Of course, the two let subscriptionX = lines won't compile, since I need to get from IGroupedObservable<bool, int> to int.
IGroupedObservable<'TKey, 'TValue> extends IObservable<'TValue>, that's how you get to the values. In your case you can do that in many ways:
// you can use SelectMany to 'flatten' the observable
groups.Where(fun i -> i.Key).SelectMany(fun o -> o :> IObservable<int>).Subscribe(doStuffWithEvenNumbers)
Note that Subscribe call take an Action, whereas in your case you defined the method as a Func. You need to remove its returned value for the call to work.
I'm trying to create a communication library that interacts with hardware. The protocol is made up of byte arrays with a header (source/destination address, command number, length) and a command specific payload. I'm creating Record Types for each of the commands to make them more user friendly.
Is there a more idiomatic way of converting an array to a record than
let data = [0;1]
type Rec = {
A : int
B : int
}
let convert d =
{
A = d.[0]
B = d.[1]
}
This can become very tedious when the records are much larger.
A few comments:
You record type definition is bogus - there should be no = in there. I assume you want
type Rec = {
A : int
B : int
}
You mentioned byte arrays, but your data value is a List. Accessing List items by index is expensive (O(n)) and should be avoided. If you meant to declare it as an array, the syntax is let data = [|0;1|]
But I wonder if records are the right fit here. If your goal is to have a single function that accepts a byte array and returns back various strongly-typed interpretations of that data, then a discriminated union might be best.
Maybe something along these lines:
// various possible command types
type Commands =
| Command1 of byte * int // maybe payload of Command1 is known to be an int
| Command2 of byte * string // maybe payload of Command1 is known to be a string
// active pattern for initial data decomposition
let (|Command|) (bytes : byte[]) =
(bytes.[0], bytes.[1], Array.skip 2 bytes)
let convert (bytes : byte[]) =
match bytes with
| Command(addr, 1uy, [| intData |]) ->
Command1(addr, int intData)
| Command(addr, 2uy, strData) ->
Command2(addr, String(Text.Encoding.ASCII.GetChars(strData)))
| _ ->
failwith "unknown command type"
// returns Command1(0x10, 42)
convert [| 0x10uy; 0x01uy; 0x2Auy |]
// returns Command2(0x10, "foobar")
convert [| 0x10uy; 0x02uy; 0x66uy; 0x6Fuy; 0x6Fuy; 0x62uy; 0x61uy; 0x72uy |]
I'm running into a bug in my code that makes me think that I don't really understand some of the details about F# and lazy evaluation. I know that F# evaluates eagerly and therefore am somewhat perplexed by the following function:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
When I call this in FSI:
> let d = getStringFromFile();;
System.ObjectDisposedException: Cannot read from a closed TextReader.
at System.IO.__Error.ReaderClosed()
at System.IO.StreamReader.ReadToEnd()
at <StartupCode$FSI_0134>.$FSI_0134.main#()
Stopped due to error
This makes me think that getStringFromFile is being evaluated lazily--so I'm totally confused. I'm not getting something about how F# evaluates functions.
For a quick explanation of what's happening, lets start here:
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd
r.Close()
s.Close()
data
You can re-write the first two lines of your function as:
let s = File.OpenRead(#"c:\eo\raw.txt")
Next, you've omitted the parentheses on this method:
let data = r.ReadToEnd
r.Close()
s.Close()
data
As a result, data has the type unit -> string. When you return this value from your function, the entire result is unit -> string. But look what happens in between assigning your variable and returning it: you closed you streams.
End result, when a user calls the function, the streams are already closed, resulting in the error you're seeing above.
And don't forget to dispose your objects by declaring use whatever = ... instead of let whatever = ....
With that in mind, here's a fix:
let getStringFromFile() =
use s = File.OpenRead(#"c:\eo\raw.txt")
use r = new StreamReader(s)
r.ReadToEnd()
You don't read from your file. You bind method ReadToEnd of your instance of StreamReader to the value data and then call it when you call getStringFromFile(). The problem is that the stream is closed at this moment.
I think you have missed the parentheses and here's the correct version:
// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =
File.OpenRead("c:\\eo\\raw.txt")
|> fun s -> let r = new StreamReader(s)
let data = r.ReadToEnd()
r.Close()
s.Close()
data