Curtis Weber
Curtis Weber

Reputation: 19

Not able to return a type that requires a cast (F#)

I am not able to return the specific case of a type to another type that just wants to hold the generic case, and not actually implement a specific example. (For example and not related to the types below, instead of returning 0.1 with units of centimeters, I am trying to return centimeters).

My type is defined as follows:

 type UnitOfMeasure =
    | Foo of decimal
    | Bar of decimal
    | Baz of decimal

I then also have:

 type BlahComponent =
    { Name : string
      Units : UnitOfMeasure }

I am given a string representation of the type of UnitOfMeasure, and I am trying to decide which UnitOfMeasure (Foo, Bar, or Baz) I have. I am using the following function:

let decideUnitOfMeasure stringRepresentationOfUnitOfMeasureType = 
    match stringRepresentationOfUnitOfMeasureType with 
      | "Foo" -> Foo
      | "Bar" -> Bar 
      | "Baz" -> Baz

Finally, I try to assign the return value of decideUnitOfMeasure to BlahComponent

        let stringRepresentationOfUnitOfMeasureType = "Foo"
        let bomComponent = {Name = "nameString"
                            Units = decideUnitOfMeasure stringRepresentationOfUnitOfMeasureType}

Trying to assign the return of decideUnitOfMeasure to Units results in the following error: The expression was expected to have type 'UnitOfMeasure' but has type '(decimal -> UnitOfMeasure)'.

It appears that by the definition of UnitOfMeasure, returning the actual case of Foo, Bar, or Baz is not possible because it requires casting UnitOfMeasure to a decimal. Is there any way to return the actual case without casting to a decimal?

Upvotes: 1

Views: 65

Answers (2)

TheQuickBrownFox
TheQuickBrownFox

Reputation: 10624

This is not about casting. It is just a type error.

Foo is a constructor for the UnitOfMeasure type. It has the type decimal -> UnitOfMeasure. This means it takes a decimal value and returns a UnitOfMeasure.

So Foo on its own is not a UnitOfMeasure, but Foo 1m is. Your Units field actually stores the number as well, and you have not provided the number.

This explains why you are seeing this error. The answer from Chad Gilbert offers a possible alternative approach, but the best approach really depends on what you're trying to do.

Upvotes: 2

Chad Gilbert
Chad Gilbert

Reputation: 36375

You may be best served by redefining UnitOfMeasure to act more like a simple enumeration, where the constructors have no arguments:

type UnitOfMeasure =
    | Foo
    | Bar
    | Baz

Now you can move the decimal value so that it is not a type parameter. You could create a record type or even keep it as a simple tuple:

type BlahComponent =
    { Name : string
      Value : UnitOfMeasure * decimal }

Upvotes: 2

Related Questions