F# forwarding of optional parameter - f#

Is it possible to forward optional parameters e.g:
type Type() =
member this.A(?param) = printfn "%d" <| defaultArg param 0
member this.B(?param) = this.A(param) // how to do this ?

Like this:
member this.B(?param) = this.A(?param = param)
Using ? when providing a named optional parameter allows you to pass an option.

Related

Is it possible to create a record type whose field is a function that takes a generic parameter, without specifying the type when creating the record?

I want to define the following record type:
type LogFuncs = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
The intention is that I can define one function of string -> unit and then use that to derive several convenience functions, like so:
let makeLogFuncs outputFunc =
let logWithLabelFunc label value = sprintf "%s: %A" label value |> outputFunc
let logWithLabelAndReturnFunc label value = logWithLabelFunc label value; value
{ LogWithLabel = logWithLabelFunc
LogWithLabelAndReturn = logWithLabelAndReturnFunc }
But, the compiler won't let me do this without specifying an <'a> when making an instance of type LogFuncs, and that's not what I want to do -- I want to be able to call this function on any 'a. (I will also want to provide related functions, hence the use of the record type.)
Is it possible to define a record type with a field containing type parameter that is not also a type parameter of the record type itself?
I don't believe it is possible to do with record types. But I can define a class which provides the mechanism I wanted:
type Logger (outputFunc: string->unit) =
member __.LogWithLabel label value = sprintf "%s: %A" label value |> outputFunc
member x.LogWithLabelAndReturn label value = x.LogWithLabel label value; value
Then I can do:
let log = new Loggery (printfn "%s")
let ``result should equal 5`` = 5 |> log.LogWithLabelAndReturn "Beans"
...and it correctly prints "Beans: 5" and returns 5.
You can make the record itself generic:
type LogFuncs<'a> = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
That also makes the makeLogFuncs generic. It's still usable, but probably not in the way you want:
(makeLogFuncs (printfn "%s")).LogWithLabel "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabel "Text" "Foo"
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Text" "Foo"
As the answer provided by Overlord Zurg implies, the OP approach is quite object-oriented, so use objects if you want to design the system in that way.

Extension method on list?

How do I define an extension method on F# lists?
A naive attempt like this results in error:
type list with
member this.abc() = 100
The correct syntax is:
type List<'a> with
member this.abc() = 100
You can use the qualified name Microsoft.FSharp.Collections.List<'a> as well, but the type abbreviation list<'a> can't be used.
That said, using module functions is more idiomatic. You should make a module function to easily combine with other functions through pipe (|>) operators:
module List =
let abc (xs: _ list) = 100
type Microsoft.FSharp.Collections.List<'T> with
member x.IsNotEmpty() = not (List.isEmpty x)
let xs = [1]
xs.IsNotEmpty

Calling generic function with 'params' from F# (Observable.StartWith)

Edit: Note that, as Daniel and latkin noted in an answer and a comment below, this question involved a bug in F# that seems to have been fixed in early 2014.
I'm trying to write a curried wrapper for Observable.StartWith. I'm using the prerelease Reactive Extensions 2.0, and the VS11 beta. My desired result would be startWith : 'a -> IObservable<'a> -> IObservable<'a>. The obvious implementation would be something like:
let startWith
(value : 'a)
(observable : IObservable<'a>)
: IObservable<'a> =
Observable.StartWith(observable, [| value |])
The intended overload of Observable.StartWith is StartWith<'TSource>(source : IObservable<'TSource>, params values: 'TSource[]) : IObservable<'TSource>.
The compiler throws a confusing error: This method expects a CLI 'params' parameter in this position. 'params' is a way of passing a variable number of arguments to a method in languages such as C#. Consider passing an array for this argument.
I am passing an array. I also tried not passing an array, by omitting the [| |], which leads to a unique-overload-resolution failure. (Presumably due to the possibility that 'a could be System.Reactive.Concurrency.IScheduler, matching the other overload.) I also tried using F# 2.0/VS2010, which gives the same result. I couldn't locate any online discussion of this sort of situation or of the compiler error message.
I can't think of any other way to implement this. Note that in cases where the type parameter can be determined, it's not a problem. For instance, let prependZero : int -> IObservable<int> -> IObservable<int> = fun n o -> o.StartWith(n) works fine. But a generic version would be nice.
It looks like a problem with type inference surrounding generic param arrays. Even a simple case, not involving overload resolution, has problems:
type A() =
static member M<'T>([<ParamArray>] args: 'T[]) = args
//None of these work
let m1 arg = A.M([|arg|])
let m2 args = A.M(args)
let m3<'T> (args:'T[]) = A.M<'T>(args)
Non-generic versions work:
type B() =
static member M([<ParamArray>] args: obj[]) = args
//Both of these are okay
let m1 arg = B.M([|arg|])
let m2 args = B.M(args)
EDIT
I emailed fsbugs and they responded that this is a bug. Here are some workarounds they suggested.
let m1 arg = A.M<obj>([|arg|])
let m2 args = A.M<obj>(args)
let m3 (args:obj[]) = A.M<obj>(args)
let m4 (arg:obj) = A.M<obj>(arg)
let m5 arg1 arg2 = A.M<obj>(arg1,arg2)
let m6 (arg1:'T) = A.M<'T>(arg1)
let m7 (arg1:'T) (arg2:'T) = A.M<'T>(arg1,arg2)
let m8 (arg1:'T) (arg2:'T) = A.M(arg1,arg2)
let m9 (arg1:'T) = A.M(arg1)
let m10<'T> arg1 arg2 = A.M<'T>(arg1,arg2)
let m11<'T> (arg1:'T) (arg2:'T) = A.M<'T>(arg1,arg2)
You do not need to wrap your single value into single element array in order for it to match the last ParamArray argument of Observable.StartWith, just scalar value is OK (these samples may help to understand why).
But then generic type of value creates an ambiguity between two available overloads for Observable.StartWith. Disambiguation can be achieved through forcing of three-agrument overload by explicitly placing the implicit type of IScheduler from the two-argument overload to the argument list, prepending the value, as below:
let startWith (value: 'a) observable =
Observable.StartWith(observable, Scheduler.CurrentThread, value)
Now your code should compile and work. A quick check confirms this:
Observable.Range(1,2)
|> startWith 10
|> fun x -> x.Subscribe(printf "%d ")
outputs as expected 10 1 2.
Update
For Rx 2.0 beta the Scheduler reference would be slightly different, the rest of the answer stays unchanged:
let startWith (value: 'a) (observable: IObservable<'a>) =
Observable.StartWith(observable, Concurrency.Scheduler.CurrentThread, value)

How to downcast from obj to option<obj>?

I have a function that takes a parameter of type object and needs to downcast it to an option<obj>.
member s.Bind(x : obj, rest) =
let x = x :?> Option<obj>
If I pass (for example) an Option<string> as x, the last line throws the exception: Unable to cast object of type 'Microsoft.FSharp.Core.FSharpOption'1[System.String]' to type 'Microsoft.FSharp.Core.FSharpOption'1[System.Object]'.
Or, if I try a type test:
member s.Bind(x : obj, rest) =
match x with
| :? option<obj> as x1 -> ... // Do stuff with x1
| _ -> failwith "Invalid type"
then x never matches option<obj>.
In order to make this work, I currently have to specify the type the option contains (e.g. if the function is passed an option<string>, and I downcast the parameter to that rather than option<obj>, the function works.
Is there a way I can downcast the parameter to option<obj> without specifying what type the option contains? I've tried option<_>, option<#obj>, and option<'a> with the same results.
By way of background, the parameter needs to be of type obj because I'm writing an interface for a monad, so Bind needs to bind values of different types depending on the monad that implements the interface. This particular monad is a continuation monad, so it just wants to make sure the parameter is Some(x) and not None, then pass x on to rest. (The reason I need the interface is because I'm writing a monad transformer and I need a way to tell it that its parameter monads implement bind and return.)
Update: I managed to get around this by upcasting the contents of the option before it becomes a parameter to this function, but I'm still curious to know if I can type-test or cast an object (or generic parameter) to an option without worrying about what type the option contains (assuming of course the cast is valid, i.e. the object really is an option).
There isn't any nice way to solve this problem currently.
The issue is that you'd need to introduce a new generic type parameter in the pattern matching (when matching against option<'a>), but F# only allows you to define generic type parameters in function declarations. So, your only solution is to use some Reflection tricks. For example, you can define an active pattern that hides this:
let (|SomeObj|_|) =
let ty = typedefof<option<_>>
fun (a:obj) ->
let aty = a.GetType()
let v = aty.GetProperty("Value")
if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
if a = null then None
else Some(v.GetValue(a, [| |]))
else None
This will give you None or Some containing obj for any option type:
let bind (x : obj) rest =
match x with
| SomeObj(x1) -> rest x1
| _ -> failwith "Invalid type"
bind(Some 1) (fun n -> 10 * (n :?> int))
I am not certain why you need to get your input as obj, but if your input is an Option<_>, then it is easy:
member t.Bind (x : 'a option, rest : obj option -> 'b) =
let x = // val x : obj option
x
|> Option.bind (box >> Some)
rest x
To answer your last question: you can use a slight variation of Tomas' code if you need a general-purpose way to check for options without boxing values beforehand:
let (|Option|_|) value =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
let opt : option<_> = (box >> unbox) value
Some opt.Value
else None
//val ( |Option|_| ) : 'a -> 'b option
let getValue = function
| Option x -> x
| _ -> failwith "Not an option"
let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")

Extending the Indexer for an existing class

Suppose I have type A with indexer implemented, e.g. type A is a library. Now I want to extend the indexer of it, e.g. here I want to add float number into the indexer.
I worked out the following code:
type A(a:int array) =
member this.Item
with get(x) = a.[x]
and set(x) value = a.[x] <- value
type A with
member m.Item with
get(x:float) = m.[x |> int]
and set(x:float) v = m.[x |> int] <- v
But it seems not working:
let a = A([| 1;2;3 |])
a.[1]
a.[1] <- 10
a.[1.0]
For the last line, I get:
Script1.fsx(243,4): error FS0001: This expression was expected to have type
int
but here has type
float
Is extending indexer possible in F#? Thanks!
This behaves differently when the type extension is defined in a separate assembly (or separate module) and when it is in the same module as the type definition.
When both are in the same module, F# compiles them into a single class and Item becomes a standard overloaded indexer - In this case, your code works as expected (and this is how you actually wrote it here).
When they are in separate modules, F# compiles the indexer as an extension member. In this case, I get the error message you described.
Adding new overloads using extension members (e.g. new method) is possible. As far I can see, the specificaton doesn't say that this shouldn't work for indexers, so I think it is a bug (can you report it to fsbugs at microsoft dot com?)
I just tried this in FSI and it seems to work.
What compiler are you using?
This is what I fed to FSI:
type A(a:int array) =
member this.Item
with get(x) = a.[x]
and set(x) value = a.[x] <- value
type A with
member m.Item
with get(x:float) = m.[x |> int]
and set(x:float) v = m.[x |> int] <- v
let a = A([| 1;2;3 |])
a.[1] <- 10
printfn "%A" a.[1.2]
This prints '10'

Resources