News for hackers. Stuff that matters.
Re:Idris 2 0.6.0 is now available for the JVM by revskill on Monday February 27, 2023 @05:51AM (#34955003)
One of most frequent usage of a type system is literal types.

Haskell, Rust,... and modern programming languages fail to do it. The only exception is Typescript.

For example, simple way to adding a extended boolean type:

type MyBoolean = null | true | false.

Or

type MyRange = 3 | 4 | 5

Curious.

Re:Idris 2 0.6.0 is now available for the JVM by quchen on Monday February 27, 2023 @06:02AM (#34955082)
Isn’t it contradictory to say it’s the most frequent usage, and in the next sentence that no modern programming language does it?
Re:Idris 2 0.6.0 is now available for the JVM by kelipso on Monday February 27, 2023 @12:20PM (#34959140)
No modern PL explicitly allows for it or type checks for it. Examples are enum types and null pointers, which are used everywhere. Implicit usage includes things like natural numbers (ints more than 0), ranges, values of a type following some rule, etc.
Re:Idris 2 0.6.0 is now available for the JVM by freilanzer on Monday February 27, 2023 @05:56AM (#34955038)
Why would I ever want to extend a type with null? That's exactly the opposite of what I want of a type system in the first place.
Re:Idris 2 0.6.0 is now available for the JVM by evomassiny on Monday February 27, 2023 @06:05AM (#34955110)
the type `null | true | false` is different from `true | false`, a type checker can assert that you handle the `null` case before using a function that wants a boolean. This is how rust handles it (with the Option<T> type).
Re:Idris 2 0.6.0 is now available for the JVM by harperlee on Monday February 27, 2023 @06:55AM (#34955384)
In particular, you may want this to signify “I dont know” with NULL, just as in SQL. See the Wikipedia page for ternary logic for more info.
Re:Idris 2 0.6.0 is now available for the JVM by alexvoda on Monday February 27, 2023 @10:36AM (#34957622)
Note that 3VL (ternary logic/3 valued logic) is one of the more criticized aspects of SQL.

3VL is a gigantic leap of of complexity over 2VL (boolean). The number of possible operations is a lot higher and the choice of operations is not standardized. To quote wikipedia: "In two-valued logic there are 2 nullary operators (constants), 4 unary operators, 16 binary operators, 256 ternary operators" And we all agree which of those 16 is named what. "In three-valued logic there are 3 nullary operators (constants), 27 unary operators, 19683 binary operators, 7625597484987 ternary operators" There is no standard meaning of the 3'rd value and of the operations involving it.

That is not to say that 3VL isn't useful. I believe the reason for the criticism of 3VL in SQL it that the system made those choices for you and those choices are not always intuitive. Therefore a null in one attribute in one relation might mean something very different from a null in another attribute in another relation.

Re:Idris 2 0.6.0 is now available for the JVM by freilanzer on Monday February 27, 2023 @07:59AM (#34955880)
Yes, so it's just about returning a (Maybe = Just bool | Nothing). No null needed.
Re:Idris 2 0.6.0 is now available for the JVM by evomassiny on Monday February 27, 2023 @08:42AM (#34956267)
exaclty, whether you call it 'Nothing' or 'null' does not matter, as long as it represents a distinct type than 'true' or 'false'
Re:Idris 2 0.6.0 is now available for the JVM by freilanzer on Monday February 27, 2023 @09:21AM (#34956679)
Null for me is a null pointer, and thus there's a difference. Anyway, if that's all you want than Haskell, etc. definitely handle that case.
Re:Idris 2 0.6.0 is now available for the JVM by nerdponx on Monday February 27, 2023 @09:49AM (#34957046)
The word "null" means "missing value" in e.g. SQL and most data science / analytics contexts. It's unfortunately an overloaded term with two different meaning. Conflating the two things is where we get the egregious mistake of using null pointers to represent null data!
Re:Idris 2 0.6.0 is now available for the JVM by avgcorrection on Monday February 27, 2023 @09:18AM (#34956644)
That doesn’t make sense. `null | false | true` is not equivalent to `Option<bool>` or `Option<false | true>`. Just like `zero | one | two` is not equivalent to `Option<one | two>`.

Assuming that `true | false` is equivalent to something like `enum { true, false }`.

Re:Idris 2 0.6.0 is now available for the JVM by evomassiny on Monday February 27, 2023 @09:28AM (#34956776)
> Just like `zero | one | two` is not equivalent to `Option<one | two>`

Isn't it ? both cases represent a type than can express 3 variants

Re:Idris 2 0.6.0 is now available for the JVM by valenterry on Monday February 27, 2023 @10:13AM (#34957321)
It is equivalent in the amount of information you can encode, but not in how you can use it.

Classical example is wrapping multiple times: Option<Option<one | two>>. If you have null | null | one | two, well... that just boils down to null | one | two.

Re:Idris 2 0.6.0 is now available for the JVM by Nullabillity on Monday February 27, 2023 @10:14AM (#34957327)
Those two types are indeed equivalent by themselves. The problem comes when you want to store them somewhere nullable. `zero | one | two | zero` flattens back down to `zero | one | two`, you can't distinguish between the two zero/null cases.

On the other hand, `Option<Option<one | two>>` allows you to distinguish between None and Some(None).

This makes union types unsound in the presence of type parameters/generics.

TypeScript supports both anyway, because Hejlsberg cares more about being able to type existing JS antipatterns than about providing a sound type system.

Re:Idris 2 0.6.0 is now available for the JVM by valenterry on Monday February 27, 2023 @11:11AM (#34958073)
> This makes union types unsound in the presence of type parameters/generics.

I'm not sure if "unsound" is a good adjective here. There are cases where this is actually desired behaviour and the rules can definitely be "sound".

For example, I might want to know what errors can appear, but not care where they come from. So `ErrorA | ErrorB` is what I want to see, not some nested structured that allows me to differentiate where ErrorA came from in case that there are multiple possible options.

Re:Idris 2 0.6.0 is now available for the JVM by ObscureScience on Monday February 27, 2023 @03:50PM (#34961688)
I did not catch from you comment if you knew, but "sound" and "unsound" are specific concepts in type theory, and they are binary properties. A system either is or is not sound.
Re:Idris 2 0.6.0 is now available for the JVM by valenterry on Monday February 27, 2023 @04:15PM (#34961953)
Yeah I know that.

So: > This makes union types unsound in the presence of type parameters/generics.

Sounds a bit strange to me. Why would union types + type parameters be generally unsafe? I doubt that that's true.

Re:Idris 2 0.6.0 is now available for the JVM by trenchgun on Tuesday February 28, 2023 @05:16AM (#34967695)
Sound type system is not one of the design goals of TypeScript

https://effectivetypescript.com/2021/05/06/unsoundness/

Re:Idris 2 0.6.0 is now available for the JVM by avgcorrection on Monday February 27, 2023 @11:26AM (#34958294)
No.

There is an isomorphism between them, but they aren’t equivalent since for one you will have to match on the `Option` first in order to see whether it is `None` or `Some(Next)` and then inspect `Next` (if `Some(…)`).

Same reason that `Nothing | Pointer` is not equivalent to `Option<Pointer>`. And it makes a huge practical difference, since the first type allows for “nothing-pointer dereference” while the second one does not.

Re:Idris 2 0.6.0 is now available for the JVM by mixedCase on Monday February 27, 2023 @12:07PM (#34958946)
> And it makes a huge practical difference, since the first type allows for “nothing-pointer dereference”

That's not how strict TypeScript works. If you have a nullable you'll need to prove to the compiler first that it is not currently null before dereferencing.

Re:Idris 2 0.6.0 is now available for the JVM by avgcorrection on Monday February 27, 2023 @12:58PM (#34959691)
Here’s what I originally replied to:

> the type `null | true | false` is different from `true | false`, a type checker can assert that you handle the `null` case before using a function that wants a boolean. This is how rust handles it (with the Option<T> type).

If the variant `null` here is handled specially in general in TS then yeah, I was wrong. However, I was mostly replying to the part about “this is how Rust handles it”.

Re:Idris 2 0.6.0 is now available for the JVM by e3bc54b2 on Monday February 27, 2023 @05:58AM (#34955059)
Usually this is done by means of Enumerated types. As someone who didn't get conventional CS education, Enums were bit of me-too for me, but the moment I realised they can be used to make invalid states unrepresentable, I was hooked.

Sure, they can be better in java, but they cover a large enough ground, and as always, most people find that sufficient.

Re:Idris 2 0.6.0 is now available for the JVM by frou_dh on Monday February 27, 2023 @08:25AM (#34956102)
What's different to typical Algebraic-Types/Sum-Types/Nü-Enums in what they showed is that it's Values being OR'd, not types. That is presumably why they are obliquely mentioning it in the comments thread of an Idris release, because Idris is known for "Dependent Types", i.e. types that depend on actual values, not just other types.
Re:Idris 2 0.6.0 is now available for the JVM by tralarpa on Monday February 27, 2023 @06:52AM (#34955360)
> Haskell

That can be done in Haskell with algebraic data types (with constructors without arguments), no?

Languages that have enums: Java, Kotlin, Dart, Ada, C (not in the first version), Pascal (not in the first version), Modula-2, Visual Basic,...

Pascal had subrange types since the first specification. For example "var month : 1..12;"

I don't know anything about Typescript but from what I have found it seems that Typescript's literal types are similar to Pascal's subrange types, i.e., the new type is compatible to the basic type it is derived from (unlike enums in the above languages, which are completely new types). But there is no runtime check.

Re:Idris 2 0.6.0 is now available for the JVM by pjmlp on Monday February 27, 2023 @08:32AM (#34956167)
I am quite sure that Pascal had enumerations since the early days.

Section "6.1.1 Scalar Types", 1973 edition:

https://www.standardpascal.org/The_Programming_Language_Pasc...

Re:Idris 2 0.6.0 is now available for the JVM by tralarpa on Monday February 27, 2023 @09:15AM (#34956622)
My bad. In the newer edition they called it "enumerated types":

http://pascal.hansotten.com/uploads/books/Pascal_User_Manual...

I didn't realize that "scalar types" in the first edition were the same thing.

Re:Idris 2 0.6.0 is now available for the JVM by afiori on Monday February 27, 2023 @08:39AM (#34956239)
Typescripts types are more similar to OCaml's variant types[0]; sadly my knowledge of OCaml's type system is less than a tenth of what I would like so I cannot give precise parallels, but in typescript you can have type A = 0 | null | Array<string>, type B = Array<number> | 0 | Array<string> and construct the type A & B = 0 | Array<string>

In general I think that it is not possible to "just" add an type intersection operator to an Hindley–Milner type system without making it either incomplete[1] or unsound[2]

[0] https://v2.ocaml.org/releases/5.0/htmlman/types.html#sss:typ...

[1][2] which typescript is

EDIT: fix typo

Re:Idris 2 0.6.0 is now available for the JVM by valenterry on Monday February 27, 2023 @10:11AM (#34957287)
It's not quite the same. You can emulate literal string types using recursive ADTs (similar lists), storing one character at the time at the typesystem.

But the ergonomics are quite different.

For example, in Scala you can do this:

    val foo: "Must be this string" = "foo" // fails to compile
In Typescript it's similar. But how would "Must be this string" look in the case of a haskell ADT?
Re:Idris 2 0.6.0 is now available for the JVM by alpaca128 on Monday February 27, 2023 @07:04AM (#34955453)
Defining `MyType = 3 | "foo" | false` needs the language to allow arbitrary type changes of a variable, and while that's possible in JS it's generally not considered a good idea.

Rust forces you to define a single enum to represent this, and then you can implement the TryFrom trait to make it directly comparable to booleans, integers etc, but without having to deal with random `undefined` values popping up everywhere.

Is it less convenient? Yes, but so are seatbelts. Sometimes you need to build software where this tradeoff is worth it.

Re:Idris 2 0.6.0 is now available for the JVM by dpratt71 on Monday February 27, 2023 @08:10AM (#34955977)
In your example, '3 | "foo" | false' is the type of the variable. What is the argument for this being a bad idea in general? You imply it's unsafe. How?
Re:Idris 2 0.6.0 is now available for the JVM by alpaca128 on Monday February 27, 2023 @07:03PM (#34963892)
> In your example, '3 | "foo" | false' is the type of the variable

Depends. TypeScript would (usually) recognize 3 being a part of that union type, but it's still a normal integer that is fully compatible with other numbers. If you add 1 to it the variable is still a number but doesn't belong to this union type anymore. You could of course argue that this is just a consequence of TS being wrapped around JS, but whether the type changes between `3` and `false` or between `3` and `4`, it does change.

In Rust this is not allowed, hence you cannot just do an addition between e.g. an enum variant and a number unless you explicitly implement the interface to make them compatible, in which case the addition produces a new value with - again - an immutable type.

If 3, "foo" and false have different types by themselves then the language has to either allow changing the type of a variable or forbid a mix of them in one type.

Re:Idris 2 0.6.0 is now available for the JVM by dpratt71 on Tuesday February 28, 2023 @11:33AM (#34971263)
Rust is more restrictive than it needs to be, in this case. What matters is whether it's possible to have a situation where the value of a variable or parameter is not concordant with its type in a given context and what it takes to make that happen.

Of course it's possible to tell the TS type checker to sod off in a given context (cast, use the "any" type, etc.), but without doing that, I think it's hard to contrive a situation such as I described above.

https://www.typescriptlang.org/play?#code/C4TwDgpgBAsiAq5oF4...

Re:Idris 2 0.6.0 is now available for the JVM by zarzavat on Monday February 27, 2023 @08:16AM (#34956031)
TypeScript lets you fit your types to your data, whatever that data is. If your data includes mixed types, then you can represent those. If I have a JSON array that mixes numbers and strings then I can write (number|string)[].

Most other statically typed languages force you to fit your data to your types. Many statically typed languages simply degenerate into dynamic typing when trying to consume external data, e.g. by representing JSON as an enumeration of every possible JSON value type.

Re:Idris 2 0.6.0 is now available for the JVM by pron on Monday February 27, 2023 @07:14AM (#34955531)
Many languages have enum types and algebraic data types (Java has both now). TypeScript is a little unique in that its core type system is much more structural than nominal (even more so than Haskell) -- and that's because of its JS heritage and interop -- but structural types have significant disadvantages as well advantages. The main advantage is succinctness as well as very powerful inference. The main downside is inscrutable type errors (if two types having the same structure are considered to be the same type, the compiler has little choice but to refer only to the structure in error messages rather than the type name).
Re:Idris 2 0.6.0 is now available for the JVM by uryga on Monday February 27, 2023 @07:33AM (#34955655)
> TypeScript is a little unique in that its core type system is much more structural than nominal (even more so than Haskell)

I'm a bit confused by the point about Haskell here -- IME Haskell's type system is pretty strictly nominal, not a lot of structural typing there

Re:Idris 2 0.6.0 is now available for the JVM by pron on Tuesday February 28, 2023 @06:59AM (#34968355)
Function types are structural in Haskell.
Re:Idris 2 0.6.0 is now available for the JVM by nequo on Monday February 27, 2023 @07:37AM (#34955688)
Your example in Haskell:

  —- ADTs in regular old Haskell
  data MyBoolean = MyNull | MyTrue | MyFalse

  —- Refinement types in Liquid Haskell
  {-@ type MyRange = {v:Int | ((v >= 3) && (v <= 5))} @-}
Re:Idris 2 0.6.0 is now available for the JVM by aranchelk on Monday February 27, 2023 @10:15AM (#34957335)
Comment is getting some hate, but it’s somewhat interesting. This is something you can’t do in standard Haskell. You can certainly do: data MyRange = My3 | My4 | My5, then write a MyRangeToInt function.

But in Haskell (AFAIK, there may be a GHC extension) you cannot say to the type checker: I want to define a new type as a subset of the inhabitants of an existing type. That seems slightly more ergonomic in some situations if you then wanted to then run other functions that work on the original type — it at least saves you the conversion step.

Regarding MyBoolean, you’re now doing the previous trick, using a subset of the inhabitants of existing types, but they’re from different types and they’re being put in an untagged union, in Haskell this would be implemented with: type MyBoolean = Maybe Bool, where “Maybe” takes any type as an argument, IMO the Haskell way is a lot cleaner.

Re:Idris 2 0.6.0 is now available for the JVM by Georgelemental on Monday February 27, 2023 @10:31AM (#34957553)
For the subcase where the literals are all members of a same overarching type (like your "MyRange" example), there has been some experimentation in this direction in Rust: https://github.com/rust-lang/rust/pull/107606
Re:Idris 2 0.6.0 is now available for the JVM by speed_spread on Monday February 27, 2023 @03:07PM (#34961207)
Anonymous union types? Scala has that. So did Ceylon.
Re:Idris 2 0.6.0 is now available for the JVM by ObscureScience on Monday February 27, 2023 @03:44PM (#34961615)
Hi, what do you mean when you say that literla types are both one of the most frequent uses of a type system, while at the same time modern languages doesn't support them?

Did you mean to say "most useful" rather than "most frequent"?