I rewrote some my f# functions definitions to static members and stuck at ref/byref parameter error:
static member bar (a : byref<int>) = Foo.bar &a
Error FS0001 This expression was expected to have type
'int ref'
but here has type
'byref<'a>'
Are there some differences with byref parameters between 'let' and static definitions?
UPD:
It's worked example of what I have changed to static member definition:
> let rec foo (a :byref<int>) =
a <- a-1
if a > 0 then
System.Console.Write(a.ToString()); foo &a
else a
;;
val foo : a:byref<int> -> int
> let mutable a = 3;;
val mutable a : int = 3
> foo &a;;
21val it : int = 0
Now it looks like that and it doesn't work. Why? :
> type Foo() =
static member bar (a : byref<int>) =
a <- a-1
if a > 0 then
System.Console.Write(a.ToString()); foo &a
else a;;
type Foo =
class
new : unit -> Foo
static member bar : a:byref<int> -> int
end
> let mutable b = 3;;
val mutable b : int = 3
> Foo.bar &b;;
Foo.bar &b;;
--------^^
stdin(71,9): error FS0001: This expression was expected to have type
'int ref'
but here has type
'byref<'a>'
int ref is type definition of F# Reference Cell, while byref<'a> is an equivalent of C# ref/out arguments (an argument passed by reference even when it's a value type).
While it's impossible to tell anything more without reproducible snippet, it's quite possible that you've conflated two types:
let myFunc(a: int ref) = a := 1
// this is wrong
let a = 1
myFunc &a
// this is right
let a = ref 1
myFunc a
Related
I have some production code that I'd like to simplify (especially in light of new SRTP F# behaviour).
The intention is to statically resolve a method/function based solely on the return type required.
A simplified version of it is this:
type TypeOf<'a> = T
type ZeroFactory = Z with
static member Zero(_: ZeroFactory,_ : TypeOf<int>) : _ = 0
static member Zero(_: ZeroFactory,_ : TypeOf<string>) : _ = ""
let inline inlineZero t =
((^T or ^N) : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
let inline zero () = inlineZero Z
let foo : int = zero ()
let bar : string = zero ()
this code compiles and does what I intend, but has always felt overly contrived.
I CAN write this:
let inline inlineZero2 t =
(^T : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
and to my eyes, that would seem to be good enough, but if i write:
let inline zero2 () = inlineZero2 Z
I get
Error FS0043 A unique overload for method 'Zero' could not be determined based on type information prior to this program point. A type annotation may be needed.
Known return type: 'a
Known type parameters: < ZeroFactory , TypeOf<'a> >
Candidates:
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<int> -> int
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<string> ->
my hunch is that all the static type parameters in the method specification have to be mentioned on the left.
I've distilled these sample types from my design:
type SomeType = T1 of int | T2 of string
type Condition = Int | String
Now, for unit testing purposes and other reasons that were distilled from here(design can't be changed), I must create SomeType values based on Condition tag.
However, my first attempt didn't compile:
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> T1 someTypeVal // warning FS0064: 'T restricted to "int"
| String -> T2 someTypeVal // error FS0001: expected "string" got "int"
Changing function signature to createSomeType'< ^T > didn't help either:
error FS0001: expected "int" got 'T
error FS0001: expected "string" got 'T
Then I tried overloading:
type detail =
static member inline dispatch (someTypeVal : int) = T1 someTypeVal
static member inline dispatch (someTypeVal : string) = T2 someTypeVal
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
| String -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
Let's disambigue, right? No, adding type annotations restricts someTypeVal to int and we're back where we started.
From C++ point of view, all that means that F# compiler doesn't support SFINAE on union cases in pattern matching.
We could use quotations or dynamic checking like this:
A)
let inline createSomeType ((someTypeVal : obj), cond) =
match box someTypeVal with
| :? int when cond = Int -> T1(someTypeVal :?> int)
| :? string when cond = String -> T2(someTypeVal :?> string)
| _ -> failwith "something happened:("
B)
type Condition = Int = 0 | String = 1
type detail =
static member inline dispatch ((someTypeVal : int), (c : int)) =
if Condition.Int = enum<Condition>(c) then
T1 someTypeVal
else
failwith "something happened:("
static member inline dispatch ((someTypeVal : string), (c : int)) =
if Condition.String = enum<Condition>(c) then
T2 someTypeVal
else
failwith "something happened:("
detail.dispatch(123, int Condition.Int) // usage
However, that's not concise and throws exceptions.
How should I implement createSomeType function so it does everything at compile-time?
P.S. The question is intentionally detailed because I couldn't find much info on this subject in one place, so someone googling will save time not repeating my errors.
EDIT:
Basically, I needed a single convenient function which uses both cond and someTypeVal with a signature like ^TVal -> Condition -> SomeType and compile time type resolution.
As #Gustavo says, IIUC, it's not possible without writing N * M overloads:
you can't expect the compiler to check for the cases contained in
cond, since those cases are values.
As stated in the comments it is not clear for me what do you want to achieve.
What's deciding? The VALUE of cont or the TYPE of SomeType?
All errors you're getting make totally sense to me. In the first attempt, the first case of the match assumes you receive an integer, so it unifies with integer. Otherwise the value should come boxed, like this:
let createSomeType' (someTypeVal : obj, cond) =
match cond with
| Int -> T1 (someTypeVal :?> int )
| String -> T2 (someTypeVal :?> string)
In the second attempt the problem is that overloading in F# doesn't work like that. It tries to resolve at the call site, unless the overload involves static constraints, which in this case doesn't.
This code will work:
type SomeType = T1 of int | T2 of string
type Condition = Int | String
type Detail = Detail with
static member ($) (Detail, someTypeVal : int) = T1 someTypeVal
static member ($) (Detail, someTypeVal : string) = failwith "something went wrong"; T2 someTypeVal
static member (%) (Detail, someTypeVal : string) = T2 someTypeVal
static member (%) (Detail, someTypeVal : int) = failwith "something went wrong"; T1 someTypeVal
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> Detail $ someTypeVal
| String -> Detail % someTypeVal
You can expect the compiler to check on the type of someTypeVal because it's a type and it will be checked at compile-time, but you can't expect the compiler to check for the cases contained in cond, since those cases are values.
A common misconception is that cases of a Discriminated Union represent types, in fact they represent different values on a single type.
If you will rely on types you don't need cond at all. Then your code will be:
type SomeType = T1 of int | T2 of string
type Detail = Detail with
static member ($) (Detail, someTypeVal : int) = T1 someTypeVal
static member ($) (Detail, someTypeVal : string) = T2 someTypeVal
let inline createSomeType' (someTypeVal : ^T) = Detail $ someTypeVal
I used operators instead of named methods because they infer the signature with the static constraints automatically, but you can use named methods as well.
Let's take a type augmentation abusive F# script below:
type AugmentMe = val _i : int
type AugmentMe with member i.I = i._i
type AugmentMe with new(i) = { _i = i }
printfn "AugmentMe instance: %i" (AugmentMe(42).I)
that, when being interpreted as a whole, yields the expected Fsi output:
>
AugmentMe instance: 42
type AugmentMe =
class
new : i:int -> AugmentMe
val _i: int
member I : int
end
val it : unit = ()
Now, if we reset Fsi session and interpret the same script, but this time line-by-line, then Fsi fails to add the type constructor with the following diagnostics for the last two script lines:
>
type AugmentMe =
class
val _i: int
end
>
type AugmentMe with
member I : int
>
~vs7894.fsx(3,30): warning FS0073: internal error: pop on empty stack during code generation, methodName = AugmentMe..ctor, m = C:\Users\gene\AppData\Local\Temp\~vs7894.fsx(3,29)-(3,39)
type AugmentMe with
new : i:int -> AugmentMe
>
~vs7894.fsx(4,35): error FS0039: The value or constructor 'AugmentMe' is not defined
After some googling the following mentioning circa 2011 has popped up, but apparently the artifact still has place.
What gives? Should I report it to fsbugs?
Thank you.
Perhaps a silly question, but why does the return value from unbox appear (in my F# Interactive session) to be typed as obj instead of the concrete type int? As far as I can understand (trying to apply existing knowledge from C#) if it's typed as obj then it's still boxed. Example follows:
> (unbox<int> >> box<int>) 42;;
val it : obj = 42
> 42;;
val it : int = 42
Function composition (f >> g) v means g (f (v)), so you're actually calling box<int> at the end (and the call to unbox<int> is not necessary):
> box<int> (unbox<int> 42);;
val it : obj = 42
> box<int> 42;;
val it : obj = 42
The types are box : 'T -> obj and unbox : obj -> 'T, so the functions convert between boxed (objects) and value types (int). You can call unbox<int> 42, because F# automatically inserts conversion from int to obj when calling a function.
On a related note: such a method is actually quite useful. I use it to deal with "the type of an object expression is equal to the initial type" behavior.
let coerce value = (box >> unbox) value
type A = interface end
type B = interface end
let x =
{ new A
interface B }
let test (b:B) = printf "%A" b
test x //doesn't compile: x is type A (but still knows how to relax)
test (coerce x) //works just fine
First off, I have a better method of dealing with this issue so it's not a problem.
However, it is something that I don't understand. Can someone explain this?
When I define the swap function as:
namespace Utilities
module Misc
let Swap (left : 'a byref) (right : 'a byref) =
let temp = left
left <- right
right <- temp
I am able to use the Swap function like this just fine.
Misc.Swap (&s.[i]) (&s.[j])
But when I define the module like:
namespace Utilities
type Misc =
static member Swap (left : 'a byref) (right : 'a byref) =
let temp = left
left <- right
right <- temp
I get the following error on both arguments:
This expression has type 'b byref but is here used with type 'a ref
How did the type inference for the caller's arguments change by moving the function into a type?
This may be an interaction with the tuple transformation that the F# compiler performs on class methods.
Reflector reports the type of Misc.Swap as:
public static void Swap<a>(ref a left, ref a right);
so we can see here that the compiler has transformed the curried arguments into tupled form.
Defining the method with tupled arguments avoids this problem:
type Misc =
static member Swap(left : 'a byref, right : 'a byref) =
let temp = left
left <- right
right <- temp
> let s = Array.init 3 (fun i -> i)
> val s : int array = [|0; 1; 2|]
> Misc.Swap (&s.[2], &s.[0])
> s;;
> val s : int array = [|2; 1; 0|]