Reputation: 1089
I want to have a specific type for an argument in a F# function:
type NewType = string*int
let testFunction (arg1:NewType) : NewType =
("resultString", 2)
testFunction ("test", 3)
I want the type of the function to be:
NewType -> NewType
But the function type is:
string*int -> NewType
What should I do to enforce the type of the argument arg1 to be "NewType"?
Upvotes: 3
Views: 172
Reputation: 10947
type NewType = string * int
is what is called a type abbreviation. It gives a name or alias to another type, but it gets erased during compilation. There is no encapsulation, no new reference and basically no new type at all.
It can serve as documentation, but F#'s compiler treats the new name and the aliased type the same. This is especially visible if you make a DLL with a public function/method which is using NewType
and try to call it from another project - you will see mixed result like in your case.
This may not be a problem if what you want to achieve is just better readability. If I see let testFunction (arg1:NewType) : NewType = ...
in the code, especially on the web like on GitHub, where there are no IntelliSense tooltips, this still gives me pretty good idea what the function takes and returns, even if the "real" type in practice is string * int
.
If you want better type safety, the usual practice is defining a single-case discriminated union which you can combine with pattern matching like this:
type NewType = NewType of (string * int)
let testFunction (NewType arg1): NewType =
NewType ("resultString", 2)
testFunction (NewType ("test", 3))
You can read more here, and in other articles from the series: https://fsharpforfunandprofit.com/posts/type-abbreviations/
Upvotes: 4
Reputation: 243051
The type declaration of NewType
is a type alias, meaning that NewType
is exchangeable with string * int
- the compiler treats them as the same thing and sometimes this means that it will report one in place of the other despite having a type annotation.
If you want a type that always has to be referred to via its full name, you'll need to define it as an explicit new type - at this point, it's probably best to use a record (which will also let you name the individual fields), but if you want something terse, you could use a single-case discriminated union instead:
type NewType = NT of (string*int)
let testFunction (NT arg1) : NewType =
NT("resultString", 2)
testFunction (NT("test", 3))
Upvotes: 4