Remix.run Logo
taeric 3 days ago

Oddly, that is precisely my problem with it as a name. I can't do any "basic algebra" operations on these things.

I say this as someone that is having to help my grade schoolers learn algebra this week, actually. :D I can squint to see where things are going and how they intersect. But it takes a lot of squinting.

dkarl 3 days ago | parent | next [-]

You can add and multiply. You have to learn what add and multiply mean with types.

There was a time when you only knew how to add and multiply with numbers, and you had to learn how to deal with things like "x". It's confusing for most kids at first, and they spend weeks getting used to it. I'd say that virtually anybody who has learned grade school algebra will learn to add and multiply types faster than they learned to add and multiply polynomials.

"Squaring" and "cubing" is an easy way to build intuition with type multiplication, because the name helps you visualize the result. If you have a type C with three values {red, green, blue} then C^2 = C x C is a "square" of values:

     (red, red)      (red, green)    (red, blue)
    (green, red)    (green, green)  (green, blue)
     (blue, red)     (blue, green)   (blue, blue)    
and C^3 = C x C x C can be visualized as a "cube" of values.

You can imagine arranging other products of finite types in the same way:

    class Foo(color: C, enabled: Boolean)
    
    // the values of Foo can be arranged in a 3x2 array
    
     Foo(red, true)    Foo(red, false)
    Foo(green, true)  Foo(green, false)
     Foo(blue, true)   Foo(blue, false)
This is analogous to how kids are taught to multiply 3 x 2.
taeric 3 days ago | parent [-]

Like I said, I can squint to see what you mean. But people tend to treat types more as what grade schools call units. And those act more as constraints on what you can do with the values. With you doing add/multiply on the values.

Note that I'm not necessarily arguing that things should change. At this point, I'd consider the name fairly well established. But, it should be noted much more heavily that you are doing algebra on the types and that this does not imply anything that you can do with the values they may represent at any time.

dkarl 3 days ago | parent | next [-]

> But people tend to treat types more as what grade schools call units. And those act more as constraints on what you can do with the values. With you doing add/multiply on the values.

Ah, I see. Yes, it doesn't make sense unless you see types as sets of values. I haven't been super deep into type theory, so I don't know how far that definition takes you, but it's the intuitive/naive starting place for understanding the idea of algebraic data types. The addition and multiplication operations are on sets of values and don't have anything to do with adding or multiplying the values themselves.

taeric 3 days ago | parent [-]

Thinking a little more about it, I would wager it would be a lot easier if people used the + and * when combining types. As things are, we say that that is what defines them being algebraic, but then never use + and *. Would be a lot easier if Maybe[String] was defined as [String + None] and similar.

(I... don't actually know if that was classically done?)

dkarl 3 days ago | parent [-]

I think they both have advantages. "Union" directly references the set operation, which helps keep you concretely grounded when learning the concept. + references a different level of abstraction, which is fine after you've internalized the operations, but probably a bit confusing to start with.

taeric 3 days ago | parent [-]

Certainly. Names are important, as much as it annoys us.

My question was more on if you could move the algebra into more focus in the types defined. Specifically, I don't think I've seen people use +/* in describing the type that often. Or ever, really.

As an example, I could see defining an Either<A, B> as (A + B) as a fairly easy to understand example that would make it very easy to see the algebra being described. Though, at that point, I confess I don't know of any common product types by name. Tuple, but that is not really satisfying to me, for some reason. (Fully ack that is a me problem.)

Could easily explain this using cards. You start with the types of Suite and Rank. With a Card being (Suite * Rank). This nicely could show that asking about a Suite is effectively asking about only part of the type for any Card.

I'll probably be slightly thinking on this longer than makes sense. :D

dkarl 3 days ago | parent [-]

When you start thinking about real programming languages, most of them don't assign any meaning to A + B or A * B, so it's up to you to construct types that act like those types.

Product types are easiest. Tuples or case classes / record types act like product types. In the case of the Foo type I defined above, it's easy to see that for each pair (c, b) in C x Boolean, there is one value of Foo, Foo(c, b), and vice-versa.

Sum types are weird, because in practice you often don't have a guarantee that two types are disjoint. Because of this, you typically don't see sum types in use without wrapper types that distinguishes the two cases. For example, the instances of Either<A, B> are typically Left(a) for some a in A or Right(b) for some b in B.

To see why this is important, consider a sum type combining Option<A> + Option<B>. The value None belongs to both types, and you can't tell if None arose as a value of Option<A> or Option<B>. In practice, this distinction matters more often than not.

For a more extreme value of non-disjoint union, consider a library function

    fetch<A>(extract: Response -> A): Either<A, Int>
that calls an HTTP API, extracts a value of type A from the response, and returns either the extracted value or, if extract fails, the HTTP status code of the response. If you extract a value of type Int from the response, the return value is Either<Int, Int>. A simple sum type Int+Int is useless here, because you won't be able to tell if 404 was the value extracted from the response or the HTTP response code.

For these reasons, Either<A, B> isn't a sum of A and B, but rather a sum of Left<Int> + Right<Int>.

One place sum types are useful is combining enumerations, because different enumerations have distinct values. For example:

    enum ClientErrors { BadRequest, NotFound }
    
    enum ServerErrors { InternalServerError, BadGateway }

    APIErrors = ClientErrors + ServerErrors
taeric 3 days ago | parent [-]

Right. Apologies, but I know the basics.

My argument is that "algebraic types" would be less confusing if you actually did algebra using the types. That is it. That is my full argument.

As it is, you have to go through hoops to show what the algebra of the types is. And how that maps to whatever subtyping rules that the popular language you are using is.

Now, my argument is not that there is not some usefulness to understanding the algebra you can do with types. Quite the contrary. I think that is very useful and people should try to understand it.

But my argument remains that without using the common operations that define algebras, namely + and *, that calling them algebraic types does not provide any help in understanding the types for people. It remains an unfortunate naming based on how people are commonly introduced to the term "algebra."

mrkeen 3 days ago | parent | prev [-]

Units also receive the add/multiply treatment.

taeric 3 days ago | parent [-]

Sort of? If you multiply two values, then the units also multiply. But if you add, they do not. They just have to be equal. No?

Even there, there is a very different thing from "adding units" and "adding values that have units."

adastra22 3 days ago | parent | prev [-]

Where are grade schoolers learning algebra?

taeric 3 days ago | parent [-]

Just google "algebra 1." Largely, it is where they learn to do symbolic math. Lots of time spent on quadratics.

adastra22 3 days ago | parent [-]

Yes, where I live (Silicon Valley) the earliest you can take algebra 1 is in middle school, and that is the accelerated pathway for the smart kids.

taeric 3 days ago | parent [-]

Ah, I see that "grade school" is sometimes defined as just to 6th grade or so. I have always used it to include all schooling up to college.

So, apologies on that. I am referring to the same stuff you are mentioning. I don't think that changes my point, at all?

adastra22 3 days ago | parent [-]

No it doesn’t, I was just curious. And yeah in the US grade school is just through 5th grade, and 6th grade in most of the rest of the world. Synonymous with elementary school AFAIK.

I think the US is far too slow in introducing algebra, with it standardly being taught in 9th grade. So that made me curious.

taeric 3 days ago | parent | next [-]

I don't remember my school days, oddly. For my kids, they don't have an "algebra" class until later. But they do basic symbolic math far sooner. I know they have done what we would call linear systems of equations as early as 6th grade. Different number systems even earlier.

My 9th grader is in an algebra class. I don't remember if it is 1 or 2.

I'm still not entirely clear I see the obvious path from the things they are doing to algebraic types.

jghn 3 days ago | parent | prev [-]

> in the US grade school is just through 5th grade, and 6th grade in most of the rest of the world

Be careful with this. Where I grew up in the US, "Grade school" was usually through 6th grade with a grade 7-8 middle school or just 1-8.