Comments by "" (@jboss1073) on "ThePrimeTime" channel.

  1. 95
  2. 61
  3. 38
  4. 25
  5. 25
  6. 19
  7. 17
  8. 12
  9. 10
  10. 10
  11. 9
  12. 9
  13. 8
  14. 7
  15. 7
  16. 7
  17. 7
  18.  @sk-sm9sh  One thing at a time. You said: "so what I think about "algebraic type system" is type system that allows expressions such as X = Y | Z In this regard C lang type system is not "algebraic" as in that it doesn't allow to do any any kind of operations on top of type system." This is all correct. However, this is also the same thing that I said. Now, hold on. I know you're talking about type algebra and I was talking about code algebra. But they are actually the same. This was proven in 1935 when Gentzen and Church's work were shown to complement each other - one though code, the other through the types. Now on to the second thing. You are conflating the notion of "algebraic types" with the notion of "turing-complete types". A turing-complete type-system does not necessarily result in an algebraic type system - Zig and Rust are not algebraic, although typescript is (I believe, from last time I checked; I'm not a js coder). For example, Zig's type system is not algebraic just because it is turing-complete, although being turing-complete makes it capable of being algebraic; however once made algebraic, that portion of the code will be "narrower" and "stricter" than the rest of Zig's type system. Hence algebraization is a formalization, an assurance that all the terms work together compatibly, and can be computed by reduction. Then you said: "So I get it that it may make some sense to describe your typesystem as algebraic. But I fail to see how it makes sense to describe a feature in your language as "algebraic effect". If your effects are compatible with your language's typesystem and if your typesystem is algebraic then by deduction I already know that it's "algebraic". " But this is wrong: Monads are NOT algebraic (they famously cannot be composed, needing a monad transformer to do so, which is essentially writing the glue code manually - the opposite of algebraic composition), hence not every effect system is algebraic. That's the wonderful thing about algebraic effects: they're finally algebraic, which means no more monad stacks, monad transformers, or even monads for that matter - they all have become obsolete because Algebraic Effects (well, handlers and prompts, technically the things behind Algebraic Effects) were proven to be the composable mathematical dual of a Monad - meaning they're just as powerful as Monads, but they're actually composable - algebraic - on top of that. Finally we have an algebraic way to deal with effects. If we had ever had that before, Monads would never have been invented. Hence the tremendous importance in naming it "algebraic effects" so as to communicate that the solution to this enormous problem of modeling side effects algebraically - composably - has finally been found.
    7
  19. 7
  20. 6
  21. 6
  22. 6
  23. 5
  24. 5
  25. 5
  26. 5
  27. 5
  28. 5
  29. 5
  30. 5
  31. 4
  32. 4
  33. 4
  34. 4
  35. 4
  36. 4
  37. 3
  38. 3
  39. 3
  40. 3
  41. 3
  42. 3
  43. 3
  44. 3
  45. 3
  46. 2
  47. 2
  48. 2
  49. 2
  50.  @curlyfryactual  " like I said, a global is just a poorly implemented object. " But it's not, and no serious Computer Scientist would say that. A global is just a variable - an address in memory with some room for you to store some bytes. An object is a closure over methods and attributes plus a virtual table. In no way can you say that "a global is just a poorly implemented object" as those two things have nothing in common with each other. Globals are not objects. Not even poorly implemented ones. They're just not objects at all. "because a global is already the enemy of concurrency. " WRONG. If it is a Mutable Transactional Variable made global then it is concurrency-safe and parallelism-safe. You are having this newbie mindset that "globals" are just what you learned, but that's not true. Many things can be global, including concurrently-safe transactional variables. In fact, that is how it is done in the industry. "I work with enough "procedural with globals" architecture these days, and it's simply a pain. It is no easier to parallelize the execution of it; in fact, far worse." That is because you are ignorant of the existence of transactional variables. You shouldn't just make a value globally available in a global variable without putting it in a Software-Transactional-Memory box. Look into Clojure, all the globals are parallel-safe and concurrent-safe. "I love declarative code as much as the next guy. Plopping in a global is certainly not that." This discussion is not about "declarative code". But even if it were, Haskell is transactional and they have concurrent-safe globals too just like Clojure. It may be only the stinky language you're using making you believe globals cannot be concurrent-safe. Not true. "May as well do it right." Throughout your answer you have demonstrated you do not have the sufficient Computer Science knowledge to do anything right. God help your clients.
    2
  51. 2
  52. 2
  53. 2
  54. 2
  55. 2
  56. 2
  57. 2
  58. 2
  59. 2
  60. 2
  61. 2
  62. 2
  63. 2
  64. 2
  65. 2
  66. 2
  67. 2
  68. 2
  69. 2
  70. 2
  71. 2
  72. 2
  73. 2
  74. 1
  75. 1
  76. 1
  77. 1
  78. 1
  79. 1
  80. 1
  81. 1
  82.  @AllanSavolainen  "so if I have a tree in C made of struct nodes, it isn't typed?" Correct, it is not typed. C does not have Computer Science Types, it has Computer Science Tags. Types are the elements of Type Theory in Computer Science, where Functions are Paths between the Types. "Could you give a concrete example of something tree-like that C cannot do?" Yes, C cannot inherently understand algebraic types such as a variant tree that might have different branches and leaves with varying structures. For example, in languages that support algebraic data types, like OCaml or Haskell, you can define a tree where each node can be either a Leaf with some value or a Branch that connects two subtrees, and the compiler will enforce those distinctions across the entire program. The compiler understands the structure and can infer or enforce the proper type usage without additional effort on your part. In C, you would need to use structs and manually manage the interpretation of the tree. C doesn’t understand the tree as a singular type—it only sees the individual fields within the structs. You would have to implement all the logic yourself, handling pointers and manually verifying which type of node you're dealing with. There's no automatic pattern matching or type safety for these variant-like structures. Hence, a concrete example of something tree-like that C cannot do is an algebraic tree where nodes can hold different types of data, such as a variant tree with different node types like Leaf Int, Leaf String, or Branch (Tree, Tree). In a language with algebraic data types, the compiler can enforce that only valid branches and leaves are combined in accordance with the defined type. It will also automatically handle different operations depending on the specific form of the node. C, on the other hand, has no concept of enforcing this; you would need to implement all validation, type checking, and interpretation manually, with no built-in language support for pattern matching or managing the tree in a type-safe manner. So in C you can never truly get a Tree, which is a well-defined Type in Type Theory within Computer Science - it is defined abstractly solely by its operations. In a language that understands true types, the tree's behavior and structure are inherently tied to the rules of the type system. Operations like traversals, insertions, or transformations are governed by the formal type system, ensuring that the operations remain valid and consistent within the type's definition. C, however, lacks the mechanisms to define such abstract types. It relies on tags (e.g., struct, enum, int, double), but those are just labels for data or groups of data; they don’t carry the semantic weight of a true type. A "tree" in C is just a collection of pointers and structs with no deeper understanding by the language of what makes a tree a tree. The compiler doesn’t know about the invariants or rules that are essential to the abstract concept of a tree; those are left entirely to the programmer to enforce manually. This is a fundamental difference between a language like C, which deals with memory layout and tagging, and languages with algebraic types that embody the abstract, rule-based nature of true types.
    1
  83. 1
  84. 1
  85. 1
  86. 1
  87. 1
  88. 1
  89. 1
  90. 1
  91. 1
  92. 1
  93. 1
  94. 1
  95. 1
  96. 1
  97. 1
  98. 1
  99. 1
  100. 1
  101. 1
  102. 1
  103. 1
  104. 1
  105. 1
  106. 1
  107. 1
  108. 1
  109. 1
  110.  @yawaramin4771  Ah, I see why you're confused. When judging feature by feature, you are right - the things you point out as examples of structural typing, such as OCaml methods, are indeed structural typing, and the things you point out as examples of nominal typing, such as turning a type into an Abstract Data Type, are indeed nominal typing. However, you're making two mistakes here: 1. classifying a language as "having nominal types" and "having structural types" (what I am doing) is not the same as identifying discrete features as being either of those two traits (what you are doing); 2. whether a language "has nominal types" or "has structural types" is not defined mainly by how a language handles its objects and its structs, but by how a language handles its functions, which is why most-if-not-all Functional Programming languages have structural types and most-if-not-all C-family, procedural, imperative and OOP programming languages have nominal types. Offering a way to "have a nominal type" in a language with structural types doesn't make it any less structural, as it cannot forget what it knows about the structure of all values, including functions, which it knows because of Hindley-Milner and other algebra-based type inferencers. Likewise offering a way to "have a structural type" in a language with nominal types doesn't make it any more structural, as it can never learn that if I say a function takes an argument "func" of type "int to string" that it should be able to take any function with such signature. In the end, OCaml and Haskell have structural types, as they allow the programmer to specify functions that take other functions by signature, and C++, C#, Java, Objective-C, Delphi, Swift, Julia and Rust all primarily use both nominal typing and nominal subtyping and as such are "nominally typed languages", or, informally, "are languages that have nominal types". Nothing I said is incorrect - I am talking about classifying the sort of typing each language is fundamentally based on, not individual features within each language.
    1
  111.  @MaxHaydenChiz  Thank you for the answer. I don't think the way I am presenting "nominal types" and "structural types" is a "weird way of looking at things", I believe you are simply applying the concept too narrowly and assuming everyone else's usage is too. Let me say it with perfectly clear words: OCaml "has" "structural types" because in it "there exists" an algebraic (i.e. structural) Hindler-Milner-plus type checker where one of its branches is "a nominal-type type-maker" via an ADT. C "has" "nominal types" because in it "there exists" not any kind of algebraic type checker, but a tag-system which is presented to the programmer-user as types. C's "types" are really no such thing from a Gentzen-Church point-of-view, meaning they cannot be used to draw equalities with code. As such, equational reasoning is impossible in languages such as C, C++, Java, etc, everyone that has nominal types as its "fundamental type-system", regardless of whether they offer "a structural-type type-maker" as one of the branches of that "fundamental type-system", inherently do not allow equational reasoning due to breaking the Gentzen-Church relationship between type and code. Please notice my interlocutor was focusing on individual features and he was right every single time. However, I am talking about the classification of the language's fundamental type-system. OCaml is fundamentally structurally typed as that is the "boss type-checker" from which you cannot run away. And inside its umbrella it lets us use a "nominal-type type-maker". On the other hand you can never get C to understand or care that a function pointer is of functions from int to int. It only cares about the return result. Because it is not mathematically a "type" system (in the Gentzen-Church way) but rather a "tag" system (those are separate mathematical objects that do what you expect from the name - tag other objects). Its "boss type-checker" is but a catalog machine without the notion of algebraic reduction. A way to show that C, C++, Java, C#, etc have nominal "boss type-checkers" and not "only individual features that can be categorized as nominally typed or structurally typed without the language being able to be judged that way as a whole" is that in C# people use interfaces (with their respective names, which the compiler cares about, hence it's a nominally typed language) instead of simply seeing the world as you see (i.e. only judging individual features, not the language) and simply picking up whatever structurally typed feature C# or Java or C++ offer and then using that instead of interfaces, a la OCaml's methods, after all that is not only much more lightweight on the programmer, it also saves them from having to name a thing (the interface) and at the same type enlists the compiler in helping to check if the proper values went to the proper places through the types mentioned in the signature, a job which otherwise in a nominally-typed language is done by the programmer-user to help the compiler's tagging "type" system, but which in structurally-typed languages like OCaml can be "recognized structurally by the compiler without the help of the programmer-user". Finally to show I am not being weird in my usage of those phrases and names, I quote: Flow dot org, slash en, slash docs, slash lang, slash nominal-structural, slash. "An important attribute of every type system is whether they are structural or nominal, they can even be mixed within a single type system. So it's important to know the difference." "A static type checker uses either the names or the structure of the types in order to compare them against other types. Checking against the name is nominal typing and checking against the structure is structural typing." "Languages like OCaml and Elm have primarily structural type systems." "We've demonstrated both nominal and structure typing of classes, but there are also other complex types like objects and functions which can also be either nominal or structural. Even further, they can be different within the same type system (most of the languages listed before has features of both). For example, Flow uses structural typing for objects and functions, but nominal typing for classes." The C2 wiki: "In structural subtyping, the answers to the above questions are dependent on the structure of types. In nominal subtyping, the answers are dependent on explicit (or sometimes implicit) declarations by the programmer." [observe that the nominal types being dependents on declarations by the programmer leads to a "tag" type system where the "types" are mere tags to objects and are not contained within an algebra known to the compiler and exposed to the user. On the other hand, structural types imply the compiler can infer the structure of the types of the code without programmer help - and usually that is done through a system of equational reasoning, usually in the form of an algebraic type system, usually a Hindley-Milner machine with some additions.] Michael Zalecki in his website: "Many functional programming languages like Haskell or Elm have a structural type system." Level Up Coding: "Languages like Java and Scala have primarily nominal type systems, whereas a language like Typescript has a structural type system." A paper entitled "An Overview of Nominal-Typing versus Structural-Typing in Object-Oriented Programming (with code examples)" By Moez A. AbdelGawad "The emphasis in this report is on defining nominality, nominal typing and nominal subtyping of mainstream nominally-typed OO languages, and on contrasting the three notions with their counterparts in structurally-typed OO languages, i.e., with structurality, structural typing and structural subtyping, respectively." "To emphasize the fact that objects in class-based OOP have class names as part of their meaning they are sometimes called nominal objects. A nominal object is always tied to the class (and superclasses) from which it was produced, via the class name information (i.e., nominal information) embedded inside the object. Having class names as part of the meaning of objects is called nominality. An OO language with nominal objects is a nominal OO language. Examples of nominal OO languages include Java [13], C# [2], Smalltalk [1], C++ [3], Scala [16], and X10 [18]." "An OO language that does not embed class names in objects is called a structural OO language. The term ‘structural’ comes from the fact that an object in such a language is simply viewed as a record containing fields and methods but with no class name information, and thus no mention of the con- tracts maintained by the object. The view of objects as records thus reflects only their structure. Examples of structural OO languages include Strongtalk [9], Moby [12], PolyToil [10], and OCaml [14]." "OO languages where types of objects are structural types (i.e., expressed as record type expressions that denote record types, with no class name informa- tion) are called structurally-typed OO languages. See the appendix for examples of both nominal and structural OO type expressions." "Class types are nominal notions, since class types with different class names are different class types that denote different sets of objects. In structurally-typed OOP, on the other hand, object type expressions express a structural view of object interfaces that does not include class names nor inheritance relation information. As such, type expressions of objects in structurally-typed OOP are the same as record type expressions. The sets of objects these type expressions denote are the same as record types well-known in the world of functional programming and among PL researchers." [notice how structural types tends to lead to functional programming naturally as recognized by this academic paper] "An OO language that does not use nominal information while deciding the subtyp- ing relation is a structurally-subtyped OO language" [part 1 of 2]
    1
  112.  @yawaramin4771  "The distinction you are trying to make here doesn't really have any practical significance in workaday OCaml programming. You use nominal or structural types as appropriate in different situations, there is no dogma or higher meaning to it." Of course there is a distinction in terms of higher worth. One of my academic quotes in my previous replies comments on how structural type systems are more difficult to program. That alone implies higher worth with regards to the proficiency of the programmer-author. But more importantly, structural types are worth more because the mathematical theory behind them is the same as the mathematical theory behind constructive mathematics, the branch of computable mathematics. Whereas "tag" type-systems like C/C++/Java etc are nothing but ad hoc "have the programmer help the compilter author regarding types" tagging systems that burden the programmer instead of helping them. It is a continuum. Towards one end, The C/C++/Java end, you as the programmer get burdened with telling your compiler what it cannot see because it is algebraically ignorant, and towards the other end, the OCaml/Haskell end, it is even possible to give the compiler just the types as have it output the code that satisfies it, showing how extreme the difference is when you are coding in a language where the compiler author offsets types for you to inform the compiler, versus in a language where the compiler author takes in the burden for you and makes a type system so good (mathematically speaking) that it can prove the Gentzen-Church equivalence both ways - if you give it code it can give you the types of that code without you telling the compiler, and if you give it types, it can give you code that abides by that type (See Oleg, djinn, Haskell). It is a world of difference. Don't try to fog this by pretending "being burdened by the type system as a programmer" is equivalent to "being helped by the type system as a programmer". Don't hide the secret that there is a "better way" than the "worse is better" world of C/C++/Java. They are worse languages than better languages like OCaml and Haskell, objectively and mathematically speaking (tags can be modeled mathematically - the type system of C, for example - but they are worthless algebraically). Please pay attention that I am not talking about features but about languages. "Structurally typed features" and "nominally typed features" have equal worth and if the latter is within the former it has equal power of deduction and constructiveness. That is why OCaml's nominal types are better than C's - in OCaml the compiler can tell you what the nominal types are without the programmer-user telling it, whereas in C the programmer-user is burdened with telling the compiler the types. I think the higher point you're missing is that "structural types" comes from "structural induction" which is what makes not only them work, but also Hindley-Milner in general. That is why OCaml and Haskell are called "structurally typed languages". See Denotational Semantics. Features that are "structural types" are not called that way because of the behavior they present, like the OCaml methods you insist on referring to. Tuples, Options and Lists are also structural. I quote F Sharp for Fun and Profit: "By 'most' F# types, I mean the core 'structural' types such as tuples, records, unions, options, lists, etc. Classes and some other types have been added to help with .NET integration but lose some of the power of structural types." It goes on to enumerate those powers: "Immutability; Pretty printing when debugging; Equality; Comparisons" Also from Denotational Semantics - A methodology for language development by David A. Schmidt: "In Pascal, a typed language, the + symbol is overloaded, because it performs integer addition, floating point addition, and set union, all unrelated operations. [this is why the programmer is burdened with telling the compiler the types] In contrast, the hd operator in Edinburgh ML is parametric. It can extract the head integer from a list of integers, the head character from a list of characters, and, in general, the head alpha from an alpha-list. hd is a general purpose function, and it is implemented as a general purpose operation. Regardless of the type of argument, the same structural [my bold] manipulation of a list is performed." If you're in doubt this "structural" refers to "structural types", page 6 has this: "Consider a description of arithmetic. It includes two equations that define the structural types of digit and operator: <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <operator> ::= + | - | x | /" Hence this is solid academic evidence from Denotational Semantics that I am doing my best to help you learn this. :)
    1
  113. 1
  114.  @yawaramin4771  Part 2 of 2 of my previous answer which did not get posted earlier for some reason: A paper entitled "An Algorithmic Framework for Recursive Structural Types" by David J. Pearce: "However, whilst numerous mainstream languages employ nominal typing, there are relatively few which employ structural typing. Examples include OCaml [28], Modula-3 [14], Strongtalk [10] and Scala [40]. One reason for this is that such languages are (typically) much harder to implement." A university: Source: "CSci 555: Functional Programming Type System Concepts" - Site: John dot cs dot olemiss dot edu tilde hcc csci555 slash notes slash TypeConcepts slash TypeSystemConcepts dot html "In a language with nominal typing, the type of value is based on the type name assigned when the value is created. Two values have the same type if they have the same type name. A type S is a subtype of type T only if S is explicitly declared to be a subtype of T." [notice here the use of "with": "in a language 'with' nominal typing". A language 'with' nominal typing 'has' nominal typing.] "For example, Java is primarily a nominally typed language. It assigns types to an object based on the name of the class from which the object is instantiated and the superclasses extended and interfaces implemented by that class." "In a language with structural typing, the type of a value is based on the structure of the value. Two values have the same type if they have the “same” structure; that is, they have the same public data attributes and operations and these are themselves of compatible types." "In structurally typed languages, a type S is a subtype of type T only if S has all the public data values and operations of type T and the data values and operations are themselves of compatible types. Subtype S may have additional data values and operations not in T." "Haskell is an example of a primarily structurally typed language. Hence I have shown that both in informal speech, as well as in formal academic speech, saying "a language has nominal types" or "a language is structurally typed" is not "weird" but instead commonplace and often useful when referring to languages instead of their individual features.
    1
  115.  @yawaramin4771  "Hindley-Milner is not a 'structural typing' system. Nobody calls it a 'structural typing' system. Show me one credible source which calls Hindley-Milner a 'structural typing' system. There is none. H-M is a type inference algorithm. It doesn't care whether the types are structural or nominal. You can easily prove this by seeing that it works with abstract types, which are by definition nominal types." Gladly. Google these two strings with the quotes: "hindley-milner" "structural induction". A key step in Hindley-Milner, namely generalization, is done by structural induction. But I also had already anticipated this question of yours and I answered it in one of my previous answers - which thank you for not reading with attention. I will quote the relevant parts for you here again: "I think the higher point you're missing is that "structural types" comes from "structural induction" which is what makes not only them work, but also Hindley-Milner in general. That is why OCaml and Haskell are called "structurally typed languages". See Denotational Semantics. Features that are "structural types" are not called that way because of the behavior they present, like the OCaml methods you insist on referring to. Tuples, Options and Lists are also structural. " and: ""Consider a description of arithmetic. It includes two equations that define the structural types of digit and operator: <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <operator> ::= + | - | x | /"" Hence if arithmetic is done through structural induction then it is also structural. Anything can be structural so long as it can be arrived at through structural induction. That's the origin of calling things "structural". You are being a novice johnny-come-lately and thinking that because "OCaml says this or that is a nominal type" that its type system isn't structural, but that "nominal type" is arrived at by the compiler through structural induction rules. Also, please bear in mind that every Type is "a nominal type". If I make a new type, it can be internally identical to an integer, but if I call it SalaryInCents then I can enforce, by creating a separate type alone, "nominal typing". Since every "purely structurally typed" language will have to offer the programmer-user a way to create new types different from the old ones for purposes of compiler verification but identical to them in structure - this is a basic activity in programming - then by your logic every programming language has "nominal types" and can only escape it if it prohibits the programmer from creating new types. Clearly that would make no sense and that nomenclature would lose its value. That's one way to know it did not originate that way. Another way to know that is to learn the history of the term from Denotational Semantics as I have nudged you now twice to research. Please kindly do it and I know you'll be excited about the new world of alternatives to Hindley-Milner (which also work through structural induction) opens up to you.
    1
  116. 1
  117. 1
  118. 1
  119. 1
  120. 1
  121. 1
  122. 1
  123. 1
  124. 1
  125. 1
  126. 1
  127. 1
  128. 1
  129. 1
  130. 1
  131. 1
  132. 1
  133. 1
  134. 1
  135. 1
  136. 1
  137. 1
  138. 1
  139. 1
  140. 1
  141. 1
  142. 1
  143. 1
  144. 1
  145. 1
  146. 1
  147. 1
  148. 1
  149. 1
  150. 1
  151. 1
  152. 1
  153. 1
  154. 1
  155. 1
  156. 1
  157. 1
  158. 1
  159. 1
  160. 1
  161. 1
  162. 1
  163. 1
  164. 1
  165. 1
  166. 1
  167. 1
  168. 1