How is the try / finally working in an asyncResult block:
let loadAndParseTrades ticker (dayDate: DateTime) : Async<Result<TradeData list, ExchangeError>> =
asyncResult {
try
// wait for loading semaphore
loadingSemaphores.WaitOne() |> ignore
// load the data
let date = dayDate.Floor (TimeSpan.FromDays(1))
let func = client.GetAsync(buildTradesRequest ticker date) |> Async.AwaitTask
let! data = getDataAsync func
// parse it and return it
return Parser.parseTrades ticker data []
finally
loadingSemaphores.Release() |> ignore
}
I have this function loading a large zip file and parsing it.
I would like to catch exceptions here and return an Error:
try
try
// wait for loading semaphore
loadingSemaphores.WaitOne() |> ignore
// load the data
let date = dayDate.Floor (TimeSpan.FromDays(1))
let func = client.GetAsync(buildTradesRequest ticker date) |> Async.AwaitTask
let! data = getDataAsync func
// parse it and return it
return Parser.parseTrades ticker data []
with ex ->
let err = Error (ExchangeError.ServiceException ex) // <- Result<'a,ExchangeError>
AsyncResult.returnError err
finally
loadingSemaphores.Release() |> ignore
but then this doesn't seem to be possible here:
Loader.fs(75, 21): [FS0193] Type constraint mismatch. The type
'Async<Result<unit,'a>>'
is not compatible with type
'Async<Result<TradeData list,ExchangeError>>'
what am I missing here?
Edit:
Added a sample that everyone can compile
let testSomethingAsync x =
async {
if x % 2 = 0 then
return Ok x
else
return Error "oh, no!"
}
let doSomethingAsync x =
asyncResult {
try
let! a = testSomethingAsync x
return a * 2
with ex ->
// none of these compile
// AsyncResult.returnError "no no no"
// Error (AsyncResult.returnError "no no no")
// return (AsyncResult.returnError "no no no")
// return (Error (AsyncResult.returnError "no no no"))
since:
let returnError x = Error x |> Async.singleton
we can assume that the lines:
Error (AsyncResult.returnError "no no no")
return (Error (AsyncResult.returnError "no no no"
will not compile for sure.
The line:
AsyncResult.returnError "no no no"
will fail to compile with:
Type constraint mismatch. The type 'Async<Result<unit,'a>>' is not compatible with type 'Async<Result<int,string>>'
And the line:
return (AsyncResult.returnError "no no no")
will fail to compile with:
This expression was expected to have type 'int' but here has type 'Async<Result<'a,'b>>'
I'm a bit lost here...
The following should work:
let doSomethingAsync x =
asyncResult {
try
let! a = testSomethingAsync x
return a * 2
with ex ->
return! AsyncResult.error "no no no"
}
Related
I am trying to use the railway programming in F# using result as described in Scott Wlaschin's book 'Domain modeling made functional'. Normally a function has the structure
let functionName parameter : Result<ResultType, ErrorType> =
result {
let! resultValue = someValidationAndTransformation parameter
return resultValue
}
But I want to return also some calculated fields, in both to Ok and the Error case. The best I could come up with was
let functionName parameter : Result<ResultType, ErrorType> * CalculatedFields =
let mutable calculatedFields = {some defaultvalue}
let result =
result {
let! resultValue = someValidationAndTransformation parameter
let calculatedField = someCalculation resultValue
calculatedFields <- {calculatedFields with calculatedField}
return resultValue
}
result, calculatedFields
This mutable field does not look nice. Is there a better way to get the calculated fields in both Ok and Error case?
I would use a match in this situation:
let functionName parameter : Result<ResultType, ErrorType> * CalculatedFields =
let result = someValidationAndTransformation parameter
let calculatedFields =
match result with
| Ok x -> someCalculation x
| Error e -> { some defaultvalue }
result, calculatedFields
I have the following F# Code that is causing a compile error:
persistence.fs(32,21): error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
The error is at the line "serializer.write...."
Any help would be appreciated.
namespace persisitence
open System.Collections.Generic
open System
open System.IO
type LocalData<'T> =
struct
val mutable elements_ : 'T list
val mutable lock_ : obj
new(e: 'T list) = { elements_ = e ; lock_ = new obj() }
end
type BinaryPersistenceOut<'T, ^W when ^W: (member write : ('T * BinaryWriter) -> unit)>(fn: string, serializer: ^W) as this =
let writer_ = new BinaryWriter(File.Open(fn, FileMode.Append))
let mutable localdata_ = new LocalData<'T>([])
let serializer_ = serializer
let NUM_SECS_IN_MIN = 60
let NUM_MSECS_IN_SEC = 1000
let NUM_MIN_BETWEEN_COMMITS = 2
let TIME_TO_WAIT = 15
let closed_ = false
let freq_ = NUM_MIN_BETWEEN_COMMITS * NUM_SECS_IN_MIN * NUM_MSECS_IN_SEC
let path_ = fn
let timer_ = new System.Timers.Timer((float) (NUM_MIN_BETWEEN_COMMITS * NUM_MSECS_IN_SEC) )
let writetofile =
fun (arg: Timers.ElapsedEventArgs ) ->
lock localdata_.lock_ ( fun () ->
if closed_ = false then
for elem in localdata_.elements_ do
serializer.write(elem, writer_)
)
do
timer_.Elapsed.Add(writetofile)
Although it'd be nice if you could invoke the write function like serializer.write(elem, writer_), you can't. You have to invoke it like this instead:
(^W: (member write : ('T * BinaryWriter) -> unit) (serializer, (elem, writer_)))
Full code block:
type BinaryPersistenceOut<'T, ^W when ^W: (member write : ('T * BinaryWriter) -> unit)> (fn: string, serializer: ^W) as this =
let writer_ = new BinaryWriter(File.Open(fn, FileMode.Append))
let mutable localdata_ = new LocalData<'T>([])
let serializer_ = serializer
let NUM_SECS_IN_MIN = 60
let NUM_MSECS_IN_SEC = 1000
let NUM_MIN_BETWEEN_COMMITS = 2
let TIME_TO_WAIT = 15
let closed_ = false
let freq_ = NUM_MIN_BETWEEN_COMMITS * NUM_SECS_IN_MIN * NUM_MSECS_IN_SEC
let path_ = fn
let timer_ = new System.Timers.Timer((float) (NUM_MIN_BETWEEN_COMMITS * NUM_MSECS_IN_SEC) )
let writetofile =
fun (arg: Timers.ElapsedEventArgs ) ->
lock localdata_.lock_ ( fun () ->
if closed_ = false then
for elem in localdata_.elements_ do
(^W: (member write : ('T * BinaryWriter) -> unit) (serializer, (elem, writer_)))
)
do
timer_.Elapsed.Add(writetofile)
Caveat: this compiles, but I have no idea if it does what you want it to do.
I'm trying to replace chained String.Replace() calls with a more functional version. Original:
let ShortenRomanNumeral (num : string) : string =
num.Replace("VIIII", "IX").Replace("IIII", "IV").Replace("LXXXX", "XC").Replace("XXXX", "XL").Replace("DCCCC", "CM").Replace("CCCC", "CD")
Functional version that works with one key value pair:
let ShortenRomanNumeral' (str : string) (k : string) (v : string) : string =
let strAfterReplace =
str.Replace(k, v)
strAfterReplace
I'm struggling to extend it to work with a list of tuples, such as
let replacements = [("VIIII", "IX"); ("IIII", "IV"); ...]
How can I write this function to apply the Replace() to the string for each key and value in the replacements list?
Fold is good. But just to demonstrate another way to do it...
// You can put the input string
// as the LAST parameter not first
let shortenRomanNumeral (k:string,v:string) (input:string) =
input.Replace(k,v)
// This allows you to do partial application like this
let replace4 = shortenRomanNumeral ("IIII", "IV")
let replace9 = shortenRomanNumeral ("VIIII", "IX")
// replace9 and replace4 have the signature string->string
// they are now simple string transformation functions
replace4 "abcIIIIdef" |> printfn "result is '%s'"
replace9 "abcVIIIIdef" |> printfn "result is '%s'"
// and they can be composed together.
// Order is important. Do 9 before 4.
let replace4and9 = replace9 >> replace4
replace4and9 "VIIII abc IIII" |> printfn "result is '%s'"
// With this approach, you can now transform a list of tuples
// into a list of string transforms using List.map
let listOfTransforms =
[("VIIII", "IX"); ("IIII", "IV");]
|> List.map shortenRomanNumeral
// and you can combine all these into one big transformation
// function using composition
let transformAll =
listOfTransforms
|> List.reduce (>>)
// finally you can apply the big function
transformAll "VIIII abc IIII" |> printfn "result is '%s'"
A fold will do the job:
let ShortenRomanNumeral' (str : string) (k : string, v : string) : string =
let strAfterReplace =
str.Replace(k, v)
strAfterReplace
let replacements = [("VIIII", "IX"); ("IIII", "IV"); ]
let replaceValues str = List.fold ShortenRomanNumeral' str replacements
replaceValues "VI VII VIIII I II III IIII" // "VI VII IX I II III IV"
Note that I only modified the last parameter of ShortenRomanNumeral' to accept tupled values.
I would like to define one of my parameters to be a C# out parameter in one of my interfaces. I realize that F# supports byref but how can I apply the System.Runtime.InteropServices.OutAttribute to one of my interface parameters?
C# Interface I am trying to replicate
public interface IStatisticalTests
{
void JohansenWrapper(
double[,] dat,
double alpha,
bool doAdfPreTests,
out double cointStatus,
out JohansenModelParameters[] johansenModelParameters);
}
Here's an example:
open System
open System.Runtime.InteropServices
[<Interface>]
type IPrimitiveParser =
//
abstract TryParseInt32 : str:string * [<Out>] value:byref<int> -> bool
[<EntryPoint>]
let main argv =
let parser =
{ new IPrimitiveParser with
member __.TryParseInt32 (str, value) =
let success, v = System.Int32.TryParse str
if success then value <- v
success
}
match parser.TryParseInt32 "123" with
| true, value ->
printfn "The parsed value is %i." value
| false, _ ->
printfn "The string could not be parsed."
0 // Success
Here's your interface, translated:
[<Interface>]
type IStatisticalTests =
//
abstract JohansenWrapper :
dat:float[,] *
alpha:float *
doAdfPreTests:bool *
[<Out>] cointStatus:byref<float> *
[<Out>] johansenModelParameters:byref<JohansenModelParameters[]>
-> unit
First of all I want to point out that I could translate the error message in uncorrect way... What is this error about? How should I write my code?
[EntryPoint]
let Main (args:string[]) =
let start = startServer (args.[0])
Console.Read()
I do not understand what should I do to let compiler be happy. Is the following code snippet correct?
let rec handle =
let handler = socket.Accept()
let rec recieveData =
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read()
0
I can't tell what your code is supposed to do because it has outside dependencies, but at a minimum your problem is indentation: whitespace in F# is significant and in particular plays a significant role in determining lexical scope. So for starters you need to fix indentation, something like
[EntryPoint]
let Main (args:string[]) =
let start = startServer (args.[0])
Console.Read()
let rec handle =
let handler = socket.Accept()
let rec recieveData =
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read()
0
Also, your employment of rec values appears unnecessary if not incorrect. And it's odd that you perform a bunch of work in the body of the handle let expression only to bind it to 0... do you mean handle or recieveData to be functions? If so maybe you intended something more like
let handle socket = //make handle a function with socket an explicit dependency
let handler = socket.Accept()
let bytesRec = handler.Receive(bytes)
let data = Encoding.ASCII.GetString(bytes,0,bytesRec)
Console.WriteLine( "Text received : {0}", data)
Console.Read() |> ignore //probably you are using Read to wait for user interaction to continue, but just ignore the result (returning unit) instead of returning 0
[<EntryPoint>]
let Main (args : string[]) =
let start = startServer args.[0]
Console.Read()