-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Literals as type parameters #608
Comments
@robkuz I think you meant
in your first code snippet. I would like to see literals in Class Type parameters, but I do not think it is possible in .NET. (I would like to be wrong.) This would open the door to a kind of dependent type. |
I have found a partial solution to this. |
I am just thinking about how this could be implemented in a backwards and .NET compatible way. Let' say we have this types type SomeGeneric<'a,'b> = {a: 'a; b: 'b}
type FixedValues = SomeGeneric<100, "foo"> The easiest way is to wrap each (literal) value in a wrapper class type ConstValue<'a>(v:'a) =
member this.Value = v and then to inherit from that class and provide a type Value_100() = inherit ConstValue(100) with
static member Zero = Value_100()
type Value_foo() = inherit ConstValue("foo") with
static member Zero = Value_foo() The initial definition would then be rewritten as type FixedValues = SomeGeneric<Value_100, Value_foo> then the creation of a given type could be as easy as let zero = LanguagePrimitives.GenericZero
let a: FixedValues = {a = zero; b = zero} |
I'll close as https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1030-anonymous-records.md seems to cover it - please reopen if not |
I'm literally interested in literals as type parameters.
That may not have been the primary focus of this issue. I will open a new language suggestion at another time when I have this worked out. |
@jackfoxy The first technical question (besides syntax) is if there is any algebraic way to combine literals, either in non-generic code (e.g.
You might get a fair way in a useful prototype if such combination is only allowed in |
@dsyme #207 helps a bit but only for those cases where records are already involved. It doesn't help for cases with DUs. In order to get those running we would need to be able to do this somehow. type Method =
| GET
| POST
type Routes =
| Foo of {|method = GET; port = 8081; path = "/foo"|}
| Bar of {|method = POST; port = 8081; path = "/bar"|}
| FooBar of {|method = POST; port = 8081; path = "/foobar"|} But I don't see how this would be possible as anonymous records seem to be a concept purely on the value level and not on the type level. The only way I foresee in the furture is with type providers for types and an extended concept for literal values [<Literal>]
let FooRoute = {method = GET; port = 8081; path = "/foo"}
[<Literal>]
let BarRoute = {method = POST; port = 8081; path = "/bar"}
[<Literal>]
let FooBarRoute = {method = POST; port = 8081; path = "/foobar"}
type Routes<'a, 'b, 'c> =
| Foo of 'a
| Bar of 'b
| FooBar of 'c
type ActualRoutes = SomeTP<Routes<_, _,_>, FooRoute, BarRoute, FoobarRoute> But appart from this and the fact that syntax & semantics are not clear and that literals would have to be extended for that to work it looses much of its appeal because there is a TP involved (which seems to be total overkill) and the closeness of the information being presented is lost. They are all scattered around. inline type expansionWhat about this idea instead. type Route(method:Method; port: int, path : string) =
member this.Method = method
member this.Port = port
member this.Path = path
type Routes =
| Foo of inherit Route(GET, 8081, "/foo")
| Bar of inherit Route(POST, 8080, "/bar")
| FooBar of inherit Route(POST, 8082, "/foobar") basically everywhere where a type is supplied (like here) type FooRoute = inherit Route(GET, 8081, "/foo") one would be able to drop in an @dsyme would that be something worthwhile examining? I don't want to reopen this issue and would then create a new issue |
Re-opening this, as Anonymous Records covers some of this, but does not address the full suggestion such as this: type Method =
| GET
| POST
type Route<'method, 'port, 'path when 'a :> Method and 'b :> int and 'path :> string> =
{
method: 'method
port: 'port
path: 'path
}
type Routes =
| Foo of Route<GET, 8081, "/foo">
| Bar of Route<POST, 8080, "/bar">
| FooBar of Route<POST, 8082, "/foobar"> |
A question I have is what should happen here? type Person: { mutable name: "John"} Can you change the contents? But nothing fits that type other than itself? |
Wondering if this can instead be erased as well? type Author = ("John" | "Arthur")
type Teacher = ("Brian" | "Arthur")
type Person = (Author | Teacher) // same as ("John" | "Brian" | "Arthur")
// future F# version 20 :P
type TeacherPersonCombo = $"{Author}-{Teacher}" // same as ("John-Brian" | "Arthur-Brian" | "John-Arthur")
typeof<Person>.Name = "string"
let p: Person = "John"
let _: string = p
match p with
| :? "John" -> ()
| :? ("Biran | "Arthur") -> () |
Seems to me that a type defined as |
I'm closing in favour of #1195 which covers the more general typescript-like design suggestion, where literals as type parameters would also be included |
I often encounter the situation where I have a data structure which in is a singleton.
So when definining that data structure (and its types) I can not only define the data types of the properties but I can also narrow down the actual values/instances for these properties.
Assume a data structure for holding the VATs for some European countries
Now this looks easy enough but is a unneeded and still error prone if for example somebody would define a new VATRULES instance and use that.
What I'd like to do is something like this
Possible variations could be
Where could this be used? For example having a fixed and statically typed dispatch table in an backend app servicing web requests
Somehow it would be necessary to allow to call any of this value constructors without a parameter (maybe unit?) and yield the propert Route instance on destructing.
One last example: assume a workflow system where within each workflow step the same record is processed. However each workflow step might access/edit only certain
parts of that record and other must not be touchd
Pros and Cons
The advantages of making this adjustment to F# are free singletons! (Other langs have free Monads we could have free singletons ;-) ) Also no option to misconfigure anything at runtime anymore (the option to misconfigure durcing compile time remains however)
The disadvantages of making this adjustment to F# are depending on the syntax/semantics this could be a breaking change. However I think this could be implemented in a non-breaking fashion
Extra information
Estimated cost (XS, S, M, L, XL, XXL): L (hard to say)
Related suggestions: (put links to related suggestions here)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
The text was updated successfully, but these errors were encountered: