Have you read

[link:blogs.msdn.com]

What are you actually trying to do (what needs solving, how will you use mbar)?

By on 8/7/2010 1:20 PM ()

It may or may not be germaine to value restrictions, but it's certainly related - the docs seem a little confusing...

The page the article references ([link:msdn.microsoft.com])
states:

> let max a b = if a > b then a else b
> The type is inferred to be 'a -> 'a -> 'a.

I beleive this should be:

- The type is inferred to be 'a -> 'a -> 'a when 'a comparison

It also states:

> The max function also works with any type that supports the greater-than operator

Is this true? I thought the "comparison" constraint asserted the presence of I(Structural)Comparison, not the definition of the single ">" operator - but even reading the spec., it's not clear to me.

Also, how does the ">" operator magically enforce the comparison constraint in the generic definition of "max"? - I could define the ">" operator to reformat my hard-drive and return an XML document, right?

thanks

By on 8/7/2010 8:28 PM ()

It may or may not be germaine to value restrictions, but it's certainly related - the docs seem a little confusing...

The page the article references ([link:msdn.microsoft.com])
states:

> let max a b = if a > b then a else b
> The type is inferred to be 'a -> 'a -> 'a.

I beleive this should be:

- The type is inferred to be 'a -> 'a -> 'a when 'a comparison

This documentation was presumably written before comparison constraints were introduced into the language, but it is no longer accurate - you are correct.

It also states:

> The max function also works with any type that supports the greater-than operator

Is this true? I thought the "comparison" constraint asserted the presence of I(Structural)Comparison, not the definition of the single ">" operator - but even reading the spec., it's not clear to me.

Also, how does the ">" operator magically enforce the comparison constraint in the generic definition of "max"? - I could define the ">" operator to reformat my hard-drive and return an XML document, right?

thanks

This statement from the documentation is still correct (depending on how you interpret it). Because max is defined in terms of the (>) operator it inherits the constraints of the (>) operator, and will therefore work on values of any type where the (>) operator also works.

You are right that you could always redefine the (>) operator to do whatever you want; in that case it's the constraints imposed by the definition of the (>) operator which is in scope where the max function is defined which will apply to its arguments. The value

1
Microsoft.FSharp.Core.Operators.(>)

has the comparison constraint on its argument type which is why that constraint propagates to the definintion of max. However if you did this:

1
2
3
let (>) x y = System.IO.Directory.Delete(@"C:\", true); true
let max a b = if a > b then a else b

(which I don't recommend) then the type of max is just

1
'a -> 'a -> 'a

, with no constraint.

By on 8/7/2010 9:08 PM ()

Thanks for the response.

> This documentation was presumably written before comparison constraints were introduced into the language, but it is no longer accurate - you are correct.

I figured. (This is from the VS2010 doc page tho' - I wonder what else is lurking...) I know docs are hard to maintain...

I'm still a little confused about this statement:

>This statement from the documentation is still correct (depending on how you interpret it). Because max is >defined in terms of the (>) operator it inherits the constraints of the (>) operator, and will therefore work
>on values of any type where the (>) operator also works.

Ok, I think I understand now. So there is the "builit-in" (>) operator *function* which there can be only one of because F# doesn't allow overloading of let-bound functions, only members - so there can be only one of those, and that standard one has the comparison constraint on it - and redefining this one has bad consequences. If I have a static member (>) overload on my own type, then that will be used, and it may or may not have a comparison constraint. And extension methods can't provide operator overloads, I can't redefine (>) on something like int's myself, so that sidesteps that fly in the ointment. Is that all correct?

By on 8/7/2010 9:48 PM ()

I'm still a little confused about this statement:

>This statement from the documentation is still correct (depending on how you interpret it). Because max is >defined in terms of the (>) operator it inherits the constraints of the (>) operator, and will therefore work
>on values of any type where the (>) operator also works.

Ok, I think I understand now. So there is the "builit-in" (>) operator *function* which there can be only one of because F# doesn't allow overloading of let-bound functions, only members - so there can be only one of those, and that standard one has the comparison constraint on it - and redefining this one has bad consequences. If I have a static member (>) overload on my own type, then that will be used, and it may or may not have a comparison constraint. And extension methods can't provide operator overloads, I can't redefine (>) on something like int's myself, so that sidesteps that fly in the ointment. Is that all correct?

Not quite. If you use a random symbolic operator (say (+++)), by default it requires that the type of one of its arguments has a static member with the same name ((+++) in this case). However, you can always provide your own let-bound definition of an operator which shadows that default behavior, in which case the operator can do whatever you want. For instance, if you have a let-bound definition:

1
let (+++) (x:int) (y:string) = false

then when that definition is in scope you will only be able to apply the (+++) operator to ints and strings (getting a bool result) regardless of whether there are any types which define a static (+++) member. If you wanted to, you could define your own let bound version of an operator that has the default behavior by using an inline definition (e.g.

1
let inline (+++) x y = ((^a or ^b):(static member (+++) : ^a * ^b -> ^c)(x,y))

), though there wouldn't be much of a point to doing so unless there were another definition of (+++) in scope that you wanted to shadow.

The F# libraries provides some built in operators which are automatically in scope (such as those in the Microsoft.FSharp.Core.Operators module. These include the (>), (<), etc. operators, which are explicitly defined to have type

1
'a -> 'a -> bool when 'a : comparison

. Therefore, it doesn't matter if you define a type which has a (>) static member - this won't be called by the (>) operator (unless you shadow the version from the standard library.

Hopefully that makes it clear what's going on in the examples below:

1
2
3
4
5
6
7
8
9
10
11
type T = A | B of int
let test1 = A > (B 4) // compiles; (>) can be used because type T satisfies the comparison constraint
let test2 = T.(>)(A, (B 4)) // doesn't compile; there is no such static member
type U() =
  static member (>)(_:U,_:U) = true
let test3 = (U()) > (U()) // doesn't compile; type U doesn't satisfy the comparison constraint
let test4 = U.(>)(U(), U()) // compiles; this static member exists
let inline (>) a b = ((^a or ^b):(static member (>): ^a * ^b -> ^c)(a, b)) // shadows normal (>) definition
let test5 = A > (B 4) // doesn't compile; no such static member on T
let test6 = (U()) > (U()) // compiles
By on 8/7/2010 11:21 PM ()

I was trying to insert into a mutable datastructure, something like

1
2
3
4
    (* incorrect code *)
    type 'a foo = E | T of 'a foo * 'a foo
    let mutable mbar = E
   mbar <- insert 123 mbar

Yes I have the blogpost, I also tried to get the approaches mentioned working.
I tried adding explicit type annotation (without success)

1
    let mutable mbar : 'a foo = E

The article also mentioned making the type non-generic, but I would like it to be generic.

I just can't figure out how to get this to work.

Thanks.

Koen

By on 8/7/2010 1:52 PM ()

I was trying to insert into a mutable datastructure, something like

1
2
3
4
    (* incorrect code *)
    type 'a foo = E | T of 'a foo * 'a foo
    let mutable mbar = E
   mbar <- insert 123 mbar

Yes I have the blogpost, I also tried to get the approaches mentioned working.
I tried adding explicit type annotation (without success)

1
    let mutable mbar : 'a foo = E

The article also mentioned making the type non-generic, but I would like it to be generic.

I just can't figure out how to get this to work.

Thanks.

Koen

Remember that a generic value bar:'a foo basically means that for any type 't that I give you, you can produce a value of type 't foo. Given this fact, and assuming that insert is defined to have type

1
'a -> 'a foo -> 'a foo

, maybe you can see why your code should not be expected to work:

1
insert 123 mbar

would have type int foo because you're inserting an int into it. However, even if mutable generic values <i>were <i>allowed, you couldn't assign a value of type <c>int foo</c> to a mutable generic variable of type <c>'a foo</c>. This is because it would no longer be possible to treat your <c>mbar</c> as a <c>'t foo</c> for all types 't (for instance, using 't = string, there would be no longer be any way to for the compiler to treat mbar as a <c>string foo</c>).

By on 8/7/2010 8:43 PM ()

You won't be able to write code like

1
mbar <- insert 123 mbar

unless that code is contained in a generic function or class that is parameterized by the generic type 'a. For example this

1
2
3
4
5
6
    type foo<'a> =  E | T of foo<'a> * foo<'a>
    let DoStuff<'a>() =
        let mutable mbar : foo<'a> = E
        mbar <- T(mbar,mbar)
    DoStuff<int>()
    DoStuff<string>()

is ok, since each call to DoStuff knows the concrete type Does that help?

By on 8/7/2010 1:58 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper