How to override ToString() in an SRTP class - f#

I am having issues creating a generic math class (here as very tiny example code a class of type Vector) that, however, is not limited to one single numerical data type for its values, but instead uses ^F as static resolved type parameter, and I expect it to be whatever the user uses to instanciate the class, such as int but also BigRational or MyCustomNumber, as long as it adheres to the constraints.
type Vector< ^F> (_values : ^F []) =
let values = _values
member inline __.Dimension = Array.length values
member inline __.Item with get i = values.[i + 1]
static member inline ( * ) (a: Vector< ^F>) (scalar: ^F) =
Vector< ^F>(Array.init (a.Dimension) (fun i -> values.[i] * scalar)
override inline __.ToString() = "{" + (values.ToString()) + "}" // <--- problem-line
My problem now with this code is, that I still don't know how to properly override the Object.ToString() method (same for how to implement IComparable, which I believe I could fix the same way).
Is this actually even possible?
Many thanks in advance!

Do not annotate the arithmetic operator's arguments with a type parameter, they will be inferred alright
Pass the arguments of the arithmetic operator as a tuple
Close the parenthesis in the implementation of the arithmetic operator
Replace the let-bound value values by a property
type Vector<'F> (_values : 'F[]) =
member val Values = _values
member inline me.Dimension = Array.length me.Values
member inline me.Item with get i = me.Values.[i + 1]
static member inline ( * ) (a: Vector<_>, scalar) =
Vector<_>(Array.init (a.Dimension) (fun i -> a.Values.[i] * scalar))
override me.ToString() = "{" + (me.Values.ToString()) + "}"

Related

Replace the value of one item of single case discriminated union?

See I have a single case discriminated union
type R = R of string * int * sting option * .....
And I got a value of R.
let r: R = getAValue ()
Now I need to replace the first item of r to an empty string and keep all other value. How to do it? Record type has the with construct
let r' = { r with Item1 = "" }
I know it can use 'pattern match' to extract all the items and create a new one. But it seems very cumbersome.
I assume you do not want to involve reflection, do you?
Then, I believe your only option would be using pattern matching. The (quite limited) burden would be defining the r-ity of your type Ras a pattern for matching.
Let's assume, for example, that your R wraps a tuple of 3 elements, i.e. has r-ity 3:
type R = R of string * int * string option
In this case all you need is to do define the following function:
let modR = function
| R(x,y,z) -> R("",y,z)
The signature of modR is R -> R, a quick check of your scenario:
let r = R("abc",1,None)
modR r
in fsi brings back
>
val it : R = R ("",1,None)
All you would need for applying the above to your specific R is set the actual r-ity of your type into the pattern.
UPDATE: As Fyodor Soikin pointed, a matching function isn't needed at all for unwrapping a single-case DU (see the docs). The sought convertion function definition may be defined as simple as
let modR (R(_,y,z)) = R("",y,z)
UPDATE2: While considering the comment from ca9163d9 I recalled just another flavor of pattern matching, namely as Pattern. Using it while implementing the sought conversion in the form of DU member gives:
type R = R of string * int * string option with
member self.modR() = let R(_,b,c) as x = self in R("",b,c)
Also #FyodorSoikin and #kaefer have pointed out in the comments that as x form isn't required for the simple DU unwrapping, similarly to terser modR function definition above:
member self.modR() = let (R(_,b,c)) = self in R("",b,c)

Can I reprogram op_Equals in F#

I'm doing a fun project in F#, which is a DSL for Camel.Net.
At some point, I want to check conditions. But the conditions entered by the programmer should evaluate to an object tree. So I want the experssion "a = b" evaluate to "SomeType.Equals(a,b)"
Is that even possible in F#?
I have this:
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
with
static member (=) (l:Macro, r:Macro) = Equals(l,r)
static member (=) (l:Macro, r:string) = Equals(l,Const(r))
and Comparison =
| Equals of Macro * Macro
Now everything in "Macro" will work as "Macro.Func" - with "Func"; a function is executed with "Message" as input param and will output the string. So the Equals(a,b) will evaluate to a string comparison during runtime.
But this code has a problem. Operator (=) does compile (it has a warning), but it can't be used as I would like.
This does not compile in the fsi:
let c1 = Header("property") = "somevalue"
I did read another question about this topic, and a bit more.
It does not answer my question.
[<NoEquality; NoComparison>] - completely shuts off the (=) operator.
[<CustomEquality; CustomComparison>] - wants you to implement an (=) operator which returns bool.
Is it even possible in F# what I want? And assuming that I can find a way, does match x with still work?
Sure, I did this reimplement to the operator in terms of System.IEquatable<T> for performance reasons:
#nowarn "86" // F# doesn't like it when you do this
[<AutoOpen>]
module FastEquals =
let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y
let inline (=) x y = eq x y
let inline (<>) x y = not (eq x y)
Just an example, you'll need to adapt for your own purposes.
Thanks to Asik's answer above, in combination with a reread of this post:
This works in the fsi:
type Message = class end
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
type Comparison =
| Equals of Macro * Macro
type Operators = Operation with
static member CompareEquals (Operation, l:Macro, r:Macro) = Equals(l,r)
static member CompareEquals (Operation, l:Macro, r:string) = Equals(l,Const(r))
#nowarn "0086" "0064"
let inline (=) (l:'N) (r:'M) = ((^T or ^N or ^M) : (static member CompareEquals : ^T * ^N * ^M -> _) (Operation, l, r))
let c1 = Header("property1") = Header("property2")
let c2 = Header("property") = "somevalue"
Note that it does not work when the static "CompareEquals" methods are located in the "Macro" type.
If you look at the signature of op_Equals:
val inline ( = ) :
l: ^N -> r: ^M -> 'a
when (Operators or ^N or ^M) : (static member CompareEquals : Operators * ^N * ^M -> 'a)
That is a really weird syntax. I don't understand the part after "when". It works, that counts.

How to define and use % as a prefix operator?

type T() =
static member (~%)(t : T) = t
let t = T()
let t' = %t // FAILS
The error message says t was expected to be of type Quotation.Expr<'a>.
% is a supposedly valid prefix operator, but is it possible to actually use it?
The reason why you are seeing this behavior is because F# does not define (~%) with static constraints like most top-level operators. It is defined as a function Quotations.Expr<'a> -> 'a. Hence, the (~%) function (which is an alias for op_Splice) you defined on type T is not resolved by uses of the top-level (~%) operator.
You can see this by the following FSI interaction:
> <# (~%) #>;;
<# (~%) #>;;
^^^^^^^^^^
C:\Users\Stephen\AppData\Local\Temp\stdin(5,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : Expr<(Expr<'_a> -> '_a)>
Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.
Thus if we redefine the top-level (~%) operator as follows, then your example will compile without error:
let inline (~%) (x : ^a) = (^a : (static member op_Splice : ^a -> 'b) (x))
but do note that quotation splicing will no longer work:
let x = <# 3 #>
<# %x #>
----^
error FS0001: The type 'Expr<int>' does not support the operator '~%'
that's because the original definition of (~%) is treated specially by the compiler for quotation splicing. Indeed, you can see in the Expr and Expr<'T> signatures that those types do not define any operators at all, let alone op_Splice.
You can see similar results with && and || infix operators. Which can be redefined (mapping to op_BooleanAnd and op_BooleanOr), but unless they are, they are treated specially by the compiler.
I'm not exactly sure why the % operator behaves like this, but you can redefine it using global let binding:
let (~%) a = -a
%10
If the operator cannot be defined as static member (I'm not sure if that's the case, or if I'm just missing something), you can still define an inline definition that invokes some static member of an object. This should give you essentially the same functionality:
// Instead of defining static member '%', we define static member 'Percent'
type T() =
static member Percent(t : T) = t
// Inline definition of '~%' that calls the static member 'Percent' of an object
let inline (~%) (x : ^T) = (^T : (static member Percent : ^T -> 'R) (x))
// Now you can use the '%t' syntax to invoke the static member
let t = T()
let t' = %t
Background: In F# quotation code, it is used for "splicing" of expressions into another expression (to build an expression that is composed from another, previously defined expression). The error message suggests that the compiler did not see your definition.
let two = <# 2 #>
let oneAndTwo = <# 1 + %two #>

Can existing types be extended to work with Seq.sum, etc?

Been working with a lot of TimeSpans recently, and have a need to get sums & averages.
However, TimeSpan defines neither operator get_Zero nor DivideByInt, so Seq.sum and Seq.average can't be used directly with this type. The following fails to compile:
open System
type System.TimeSpan
with
static member Zero with get() = TimeSpan()
static member (/) (n:DateTime, d:int) = DateTime( n.Ticks / (int64) d )
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
Error: The type 'TimeSpan' does not support any operators named 'get_Zero'
Error: The type 'TimeSpan' does not support any operators named 'DivideByInt'
Warning: Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.
Is there some F# magic that can define these operators on an existing type?
I know the following will work (and should be more efficient to boot), but I'm still curious about the above so I can add it to my toolbox for use with other types.
let sum = TimeSpan( ts |> Seq.sumBy (fun t -> t.Ticks) )
let avg = TimeSpan( let len = ts |> Seq.length in sum.Ticks / int64 len )
As far as I know, static member constraints (that are used by functions like Seq.sum) are not able to discover members that are added by type extensions (essentially, extension methods), so I don't think there is a direct way of doing this.
The best option I can think of is to creat a simple wrapper around the System.TimeSpan struct. Then you can define all the required members. The code would look like this:
[<Struct>]
type TimeSpan(ts:System.TimeSpan) =
member x.TimeSpan = ts
new(ticks:int64) = TimeSpan(System.TimeSpan(ticks))
static member Zero = TimeSpan(System.TimeSpan.Zero)
static member (+) (a:TimeSpan, b:TimeSpan) =
TimeSpan(a.TimeSpan + b.TimeSpan)
static member DivideByInt (n:TimeSpan, d:int) =
TimeSpan(n.TimeSpan.Ticks / (int64 d))
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
I called the type TimeSpan, so it hides the standard System.TimeSpan type. However, you still need to write ts.TimeSpan when you need to access the underlying system type, so this isn't as nice as it could be.
Mhh the following is rather ugly, but it works. Does it help? I define a wrapper for TimeSpan that can implicitly be converted back to a TimeSpan.
type MyTimeSpan(ts : TimeSpan) =
member t.op_Implicit : TimeSpan = ts
static member (+) (t1 : MyTimeSpan, t2 : MyTimeSpan) =
new MyTimeSpan(TimeSpan.FromTicks(t1.op_Implicit.Ticks + t2.op_Implicit.Ticks))
static member Zero = new MyTimeSpan(TimeSpan.Zero)
static member DivideByInt (t : MyTimeSpan, i : int) =
new MyTimeSpan(TimeSpan.FromTicks(int64 (float t.op_Implicit.Ticks / float i)))
let toMyTS ts = new MyTimeSpan(ts)
let l = [TimeSpan.FromSeconds(3.); TimeSpan.FromSeconds(4.)]
|> List.map toMyTS
|> List.average

Variable length tuples in f#

Is it possible to write a function to accept a tuple of variable length? I'm trying to write a method that can be called like this:
let a = sum(1,2)
let b = sum(1,2,3)
EDIT: Could it be interpreted as a function call with params? Or would the method need to be written in c#:
double sum(params object[] double) {
...
}
No - tuples are by definition not variable length, and to write a function like this you'd need something like template metaprogramming in C++ - and there isn't such a thing in F#; let inline won't help you there either.
Of course, if you take a list instead, it won't look that much different:
sum[1; 2]
sum[1; 2; 3]
#PavelMineav is right, you can't do it, but note that members can be overloaded, a la
type Foo() =
member this.sum(x,y) = x + y
member this.sum(x,y,z) = x + y + z
let foo = new Foo()
printfn "%d" (foo.sum(1,2))
printfn "%d" (foo.sum(1,2,3))
whereas let-bound functions cannot.

Resources