You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I saw your note saying there is an unknown type somewhere in JSError but I couldn't find it or determine if it resolves the issue here. I've been experimenting with some code that makes use of the unknown type. Below is an example. Notice the syntax error at the very bottom. Is it possible to create an unknown type that you can safely assign anything to without requiring the developer to do something like Unknown.make? In TypeScript there are two top-types any and unknown(see here). In ReScript whenever I see 'a that looks like any to me. I've seen ReScript type definitions that use something like <+'a> and thought maybe that technique would help but I don't understand it.
In the example below, I want developers to author instances of Pattern. It is safest to author these when isTypeOf takes an Unknown.t because if it were just an 'a then the developer, if not careful, could treat the function parameter like a string or anything when it really isn't, and this can cause run-time crashes. But when using a Pattern I want to be able to pass in anything and not have to manually covert it to unknown using Unknown.make which is kind of weird. So from inside the isTypeOf the parameter should look like Unknown.t but when calling it from the outside I want it to accept an 'an any.
If this isn't possible, I wonder if we should have a unknown top-level type exposed in the language. This would be useful to provide more safe external functions; make them return an unknown if we don't know exactly what we're going to get. We could use this in Object module of Core when we're getting property values. And maybe genType exports these as unknown without any shims.
moduleUnknown= {
typetexternalmake: 'a=>t="%identity"
}
moduletype Pattern= {
typetletisTypeOf: Unknown.t=>bool
}
moduleNonEmptyString: Pattern= {
typet=stringletisTypeOf= (u: Unknown.t) =>switchu->Js.Types.classify {
| Js.Types.JSString(s) =>s->Js.String2.trim->Js.String2.length > 0
| _=>false
}
// Unsafe and unpredictable if not a string// let isTypeOfUnsafe = u => u->Js.String2.trim->Js.String2.length > 0
}
letx=45->Unknown.make->NonEmptyString.isTypeOf// Works but weirdlety=45->NonEmptyString.isTypeOf// Syntax error!
The unknown type is in the compiler, it's called unknown and is a predefined type.
The make function is correct, as any value has unknown type. There's no such function in the language, but could be added to Core.
Perhaps this should be moved to Core.
I'm advocating to expose the unknown type in the language itself maybe as a keyword like string or int. Definitely not specific to the Core library. And also that the user never needs to call some "make" function. Instead the compiler allows assignment to unknown always because it is always safe. Just like TypeScript. It is awkward to use a make function for that.
In case I wasn't clear... in my own code I made my own Unknown.t that is just an abstract type that does nothing. Using it is awkward and weird because if you want to call a function that has a parameter of type unknown you have to call Unknown.make which doesn't really do anything and always succeeds. The feature I'm missing is the ability to assign any value to an unknown without calling a make function. That is what requires compiler support.
A bunch of functions take a 'a which is anything. It would be weird if you had to first call Anything.make to use those functions. Same with unknown.
Ok I wish I understood what you're saying. I'm curious how you envision this in Core. Will there be a Types.unknown type or something like that? Will I be able to assign values to unknown without explicitly calling a make function? Does this mean Core is required to use unknown?
So if I have a function that takes an Unknown.t and I want to call it I will ALWAYS have to do something like this below.
let f = (u:Unknown.t) => ...
let something = "abc"
f(something->Unknown.make)
Or can you make the compiler do something special with Unknown, like TypeScript, so anything can be assigned to it without calling the make function? I understand you are probably overwhelmed doing almost all the work on the compiler - the most important part of ReScript - and so I can see why this might not be top priority. But I just want to make sure you understand what I'm asking for here. Unknown.t is special because it might be the only type, other than anything, you can safely assign anything to. Without that automatic safe assignment it becomes cumbersome/awkward to use.
So if I have a function that takes an Unknown.t and I want to call it I will ALWAYS have to do something like this below.
let f = (u:Unknown.t) => ...
let something = "abc"
f(something->Unknown.make)
Or can you make the compiler do something special with Unknown, like TypeScript, so anything can be assigned to it without calling the make function? I understand you are probably overwhelmed doing almost all the work on the compiler - the most important part of ReScript - and so I can see why this might not be top priority. But I just want to make sure you understand what I'm asking for here. Unknown.t is special because it might be the only type, other than anything, you can safely assign anything to. Without that automatic safe assignment it becomes cumbersome/awkward to use.
Thar's correct. One can add a subtyping rule (so one does not need to call make), but really it seems overkill for this use.
It's up to debate, but likely the ecosystem might benefit more from a 10X influx of beginners than 2X influx of experts.
So it's not clear how to balance these things.
The beginner will go for Unknown.make and the more experienced person discovers they can skip it. Maybe a compiler warning/info tells people that function is deprecated/unnecessary so everyone figures it out.
Very interesting case of Some - hadn't thought of that.
If someone writes a function that should take an unknown they can write it to take an any which makes using that function very convenient. They just have to be careful when writing that function not to treat the parameter as if it is something it is not and generate run-time exceptions. When using external functions that return unpredictable values it is safer to mark them as returning an unknown. So using unknown helps beginners do the right thing while it won't matter much for experts.
I was imagining a future version of unions would have people author modules with the shape like this. Each option in the union would be a module name. So discrimination and pattern matching happens with code the user writes which is totally customizable based on typeof, instanceof, or a JSON parsing library. Any value of type PositiveInt.t, Success.t, Failure.t, etc. could be safely assigned to the union. And the equals enables optimized equality testing. Literals could have special syntax. Each built-in module like Int, String etc. could have these functions (just like Array.isArray) so you could make a union of string and ints and bigints without any special work in the compiler. In this scenario, developers would write functions that take an unknown.
module Kind = {
type t
let isTypeOf : unknown => bool
let equals : (t,t)=>bool
type t = PositiveInt | String | Success | Failure
I agree we have no idea who would use unknown types and need to see how things play out to determine the importance of it.
HUGE misunderstanding here. Maybe I never actually tried typing unknown into my Rescript and so I created my own thing Unknown.t which was totally unnecessary. I created a PR on Core that Glenn looked at and he didn't tell me unknown already existed. I raised an issue about it on Core and no one commented until much later except for you, and I thought you meant it was a type buried away somewhere inside the compiler but not actually exposed. It isn't in any of the online docs. I've had other interactions with people and no one said "hey that already exists". I was shocked that tonight I could just type unknown anywhere and it works. I read your comments again above and you make it clear this exists as a predefined type. You must think I'm crazy or "what the hell is this guy saying we need this thing for when it already exists!"
Activity
cristianoc commentedon Apr 10, 2023
The unknown type is in the compiler, it's called
unknownand is a predefined type.The
makefunction is correct, as any value has unknown type. There's no such function in the language, but could be added to Core.Perhaps this should be moved to Core.
jmagaram commentedon Apr 10, 2023
I'm advocating to expose the unknown type in the language itself maybe as a keyword like string or int. Definitely not specific to the Core library. And also that the user never needs to call some "make" function. Instead the compiler allows assignment to unknown always because it is always safe. Just like TypeScript. It is awkward to use a make function for that.
jmagaram commentedon Apr 10, 2023
In case I wasn't clear... in my own code I made my own
Unknown.tthat is just an abstract type that does nothing. Using it is awkward and weird because if you want to call a function that has a parameter of typeunknownyou have to callUnknown.makewhich doesn't really do anything and always succeeds. The feature I'm missing is the ability to assign any value to an unknown without calling a make function. That is what requires compiler support.A bunch of functions take a
'awhich is anything. It would be weird if you had to first callAnything.maketo use those functions. Same withunknown.cristianoc commentedon Apr 10, 2023
It's clear. This is for Core.
jmagaram commentedon Apr 10, 2023
Ok I wish I understood what you're saying. I'm curious how you envision this in Core. Will there be a
Types.unknowntype or something like that? Will I be able to assign values to unknown without explicitly calling amakefunction? Does this meanCoreis required to useunknown?cristianoc commentedon Apr 10, 2023
Sorry if I was unclear. Type unknown exists already.
What would go in Core is:
in
Unknown.res. Just a little convenience.jmagaram commentedon Apr 10, 2023
So if I have a function that takes an Unknown.t and I want to call it I will ALWAYS have to do something like this below.
Or can you make the compiler do something special with Unknown, like TypeScript, so anything can be assigned to it without calling the
makefunction? I understand you are probably overwhelmed doing almost all the work on the compiler - the most important part of ReScript - and so I can see why this might not be top priority. But I just want to make sure you understand what I'm asking for here.Unknown.tis special because it might be the only type, other thananything, you can safely assign anything to. Without that automatic safe assignment it becomes cumbersome/awkward to use.cristianoc commentedon Apr 10, 2023
Thar's correct. One can add a subtyping rule (so one does not need to call
make), but really it seems overkill for this use.cristianoc commentedon Apr 10, 2023
If people start using unknown very extensively, then the subtyping rule can be considered.
cristianoc commentedon Apr 10, 2023
Notice a more compelling, but analogous, request would be to automatically promote (certain) values to option so one does not need to use
Some.cristianoc commentedon Apr 10, 2023
(some) people would likely object that it is "too magic" -- basically optimising the expert's use case at the expense of possibly confusing beginners
cristianoc commentedon Apr 10, 2023
It's up to debate, but likely the ecosystem might benefit more from a 10X influx of beginners than 2X influx of experts.
So it's not clear how to balance these things.
jmagaram commentedon Apr 10, 2023
The beginner will go for
Unknown.makeand the more experienced person discovers they can skip it. Maybe a compiler warning/info tells people that function is deprecated/unnecessary so everyone figures it out.Very interesting case of
Some- hadn't thought of that.If someone writes a function that should take an
unknownthey can write it to take ananywhich makes using that function very convenient. They just have to be careful when writing that function not to treat the parameter as if it is something it is not and generate run-time exceptions. When using external functions that return unpredictable values it is safer to mark them as returning anunknown. So usingunknownhelps beginners do the right thing while it won't matter much for experts.I was imagining a future version of unions would have people author modules with the shape like this. Each option in the union would be a module name. So discrimination and pattern matching happens with code the user writes which is totally customizable based on
typeof,instanceof, or a JSON parsing library. Any value of type PositiveInt.t, Success.t, Failure.t, etc. could be safely assigned to the union. And theequalsenables optimized equality testing. Literals could have special syntax. Each built-in module likeInt,Stringetc. could have these functions (just likeArray.isArray) so you could make a union of string and ints and bigints without any special work in the compiler. In this scenario, developers would write functions that take an unknown.I agree we have no idea who would use
unknowntypes and need to see how things play out to determine the importance of it.jmagaram commentedon Apr 11, 2023
HUGE misunderstanding here. Maybe I never actually tried typing
unknowninto my Rescript and so I created my own thingUnknown.twhich was totally unnecessary. I created a PR on Core that Glenn looked at and he didn't tell meunknownalready existed. I raised an issue about it on Core and no one commented until much later except for you, and I thought you meant it was a type buried away somewhere inside the compiler but not actually exposed. It isn't in any of the online docs. I've had other interactions with people and no one said "hey that already exists". I was shocked that tonight I could just typeunknownanywhere and it works. I read your comments again above and you make it clear this exists as a predefined type. You must think I'm crazy or "what the hell is this guy saying we need this thing for when it already exists!"Should this be added to the docs as a built-in type?
https://rescript-lang.org/docs/manual/latest/primitive-types
cristianoc commentedon Apr 11, 2023
Glad it's clear now.
Yes a PR to the docs would be great.
jmagaram commentedon Apr 11, 2023
Please see rescript-lang/rescript-lang.org#670