
Examples of Incorrect Abstractions in Other Languages - bshanks
https://www.reddit.com/r/haskell/comments/glz389/examples_of_incorrect_abstractions_in_other/
======
andolanra
Some of these are arguably "incorrect abstractions", but others are just
design decisions that are being treated as "incorrect" because they don't
follow Haskell's design sensibilities. A perfect example is the complaint
about _sum_ in Python:

    
    
       >>> sum(["a", "b"], start="")
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: sum() can't sum strings [use ''.join(seq) instead]
    

I don't think this is incorrect at all! There's a very good reason to use
_join_ instead of _sum_ for adding strings together: _join_ is a lot more
efficient than simply adding strings together, because it will precompute the
amount of space needed for the resulting string at once and then put all the
strings into the buffer at once, whereas using _sum_ in the naïve way
suggested would do a different append for each element. These are just
different tools for different purposes!

Could you design a _sum_ that automatically looked at its argument type and
deferred to _join_ for strings? Sure! And that'd be a valid design decision as
well. But the specific choice Python made here is motivated and defensible:
it's only "incorrect" if your personal definition of "correct" is "built with
the same algebraic sensibility that Haskell's designers had," but at that
point, all other languages are "incorrect" because they're not Haskell!

~~~
tines
Special cases like this make generic code very hard to write. If I'm writing a
library that needs to handle user-provided data agnostic of type and I want to
do a sum operation, now I have to add checks all over the place for whatever
special cases python has.

~~~
wirrbel
I think never have I written generic code for summing up numbers AND
concatenating strings. Even the Haskell people would agree I think. So the
design problem is in overloading `+` for string and list concatenation in
Python in the first place.

But I guess in terms of strings let's take a moment and remember the one truly
good design decision that Python devs made about strings (which isn't _usual_
for an imperative language). Strings are immutable by default.

~~~
lmm
> I think never have I written generic code for summing up numbers AND
> concatenating strings. Even the Haskell people would agree I think.

Erm, Haskell people write code that can do this all the time - you write stuff
in terms of generic monoid and then it just does. I can even think of a
concrete case where I used those two examples: I had essentially a report that
looped over a bunch of rows and accumulated something as a secondary effect,
and in one client's case that was a string explaining what had happened, and
in another client's case that was a count of how many things had been
processed.

~~~
still_grokking
So you've written (of course type safe) generic code that was in principle
able to achieve the same effect as described in
[https://stackoverflow.com/questions/9032856/what-is-the-
expl...](https://stackoverflow.com/questions/9032856/what-is-the-explanation-
for-these-bizarre-javascript-behaviours-mentioned-in-the) ? ;-)

Batman!

~~~
rowanG077
No. He was talking about monoids. The content in the link you are talking
about aren't monoids.

~~~
still_grokking
Do the implementation details matter if you could achieve the same result?

~~~
rowanG077
It's not about the implementation details. It's about that the things that are
mentioned in the link make no logical sense. The addition operator allows
values of different types to be added together and even worse the resulting
values type isn't required to have the type of either operands.

It's not possible to write the generic code since there are basically no
constraints. Generic code is all about constructs with certain invariants and
use those invariants to create something new. Javascript addition basically
has no invariants.

~~~
still_grokking
>It's not about the implementation details. It's about that the things that
are mentioned in the link make no logical sense. The addition operator allows
values of different types to be added together and even worse the resulting
values type isn't required to have the type of either operands.

I would argue that that's not true.

JS is a "mono-typed" language and there is therefore only one type of things
you can operate on and return as a result.

>It's not possible to write the generic code since there are basically no
constraints. Generic code is all about constructs with certain invariants and
use those invariants to create something new. Javascript addition basically
has no invariants.

That's backwards to my point. I tried to point out that having generic code
which can operate on (almost) arbitrary data structures mixing for example
natural numbers or lists of arbitrary things will lead in the end to the same
puzzling results as the "classical JS-Batman" example.

Don't get me wrong, generics are a great tool! But imho it's easy to
"overstretch" their usage until a point where things become "so generic" that
your functions can operate on "almost everything" but the results become as
surprising as the alleged "JS insanities".

Something like the Monoid abstraction is incredibly powerful (most languages
can't express such an abstraction in a sensible way even). Using such a
powerful tool to abstract over some very concrete and banal things (that would
be handled better separately as they're "not the same", no matter there may be
a deep mathematical connection) is not a good architecture imo. If your code
handling "concrete things" is written in a very generic and abstract way you
won't have any "normal" tools left on your belt in case you would need to
abstract over that stuff you've just written (at this point you could enter
type-level programming or something that will achieve something similar but
you will than end up with laughable complex code to handle in the end banal
business logic).

[https://en.wikipedia.org/wiki/Rule_of_least_power](https://en.wikipedia.org/wiki/Rule_of_least_power)

~~~
rowanG077
> JS is a "mono-typed" language and there is therefore only one type of things
> you can operate on and return as a result.

Javascript has a type system that is enforced at run-time.

> That's backwards to my point. I tried to point out that having generic code
> which can operate on (almost) arbitrary data structures mixing for example
> natural numbers or lists of arbitrary things will lead in the end to the
> same puzzling results as the "classical JS-Batman" example.

I don't agree. The entire point of the link you posted was the insanity of the
semantic of the addition operations in Javascript. Monoids are very simple and
well defined. The javascript insanity would need pages upon pages of
explanations because basically everything is a special case. In contrast
monoids can be fully explained in a two sentences.

~~~
still_grokking
>> JS is a "mono-typed" language and there is therefore only one type of
things you can operate on and return as a result.

>Javascript has a type system that is enforced at run-time.

Nobody questions that JS has "runtime type-checks". But that's irrelevant to
my point. I've referenced of course this here:

[https://existentialtype.wordpress.com/2011/03/19/dynamic-
lan...](https://existentialtype.wordpress.com/2011/03/19/dynamic-languages-
are-static-languages/)

>Monoids are very simple and well defined.

Sure. Just define an instance for a type that models a JS runtime-object,
let's see what happens… ;-)

But I see, my point is still not explained well: What I'm trying to say is
that using abstractions that are so powerful that the resulting code can
operate on (almost) arbitrary "unrelated" things using that code will lead to
the same _kind_ of puzzling outcomes as "JS Watman".

(And like I said, a deep mathematical relation doesn't make things "related"
in a practical sense automatically. For example when it comes to "correct"
domain modeling of business cases where you _explicitly_ want to treat
different parts of your system separately; as this results in a less
"entangled" architecture. Gluing together parts that are unrelated through
clever code on the other hands side creates maintenance hell where everything
depends on everything, but you can't even see what depends on what as
everything is generic; that's the opposite of good software engineering, imho.
To throw a few phrases: "Divide and conquer!" "Use the right tool for the
job!" "Use the least powerful tool that gets the job done well!").

------
lhorie
Fun trivia, the promise absorption thing was dismissed when it was brought up,
with the argument that the "ship had already sailed on that" and that the
arguments for supporting promises of promises were too academic (in the
detractors' words, it was "fantasy land").

Which then resulted in the creation of a specification called fantasy-land[1]
as a form of mockery towards the "incorrect abstractions are fine" attitude.
The goal of this spec is to standardize on functional algebra nomenclature in
JS.

[1] [https://github.com/fantasyland/fantasy-
land](https://github.com/fantasyland/fantasy-land)

~~~
k__
lol, I knew fantasy-land, but I didn't know the story, thanks!

I have to admit, when I read stuff by hardcore FP proponents like in that
Reddit thread, I also feel the urge to call it fantasy land.

------
legerdemain
This is a thread from r/haskell. I think a fair summary of the contents is
"things Haskell people dislike about other languages."

~~~
smcl
To me it looks like someone who wants to compile a list of arguments they can
refer to when they are having an argument about programming languages

------
christophilus
> And yet, for a long time, it lacked a function analogous to < _> , which is
> strange when considered from a "folds are Applicative" perspective.

Man. I enjoyed hacking around with Haskell back in the day, but I found it to
be a write only language with forms like <_> making me stop and stare too
often.

Also, after maybe 15 years of working in statically typed languages, I also
decided that ultimately, I prefer dynamically typed languages. Still have yet
to use my favorite (Clojure) in production, though.

~~~
outworlder
> Also, after maybe 15 years of working in statically typed languages, I also
> decided that ultimately, I prefer dynamically typed languages. Still have
> yet to use my favorite (Clojure) in production, though.

I like to have types when I need types. If I want to write a numeric value in
a json output I'm creating that will be consumed by some other system, I want
that thing to represent exactly that and spit an error if I ever try to assign
something else, such as a string. But more than that, it should be easy to
define my own types. I need to be able to say, "this number is not just any
number, this is actually a temperature measurement in Celsius" or "this is
talking about screen coordinates, not world coordinates". And then only be
able to send that to the correct functions – or treat as a normal number if
I'm doing basic math.

But most of the time, most people don't care about types. We want to say "do
this operation across all elements of this array". If I trust whatever lower
level function that added the properly typed elements in the first place, now
I can focus on manipulating them. Glue code is very amenable to dynamic types.

Haskell has a very rich type system (maybe too rich?), but it also allows you
to say that function receives an A and returns [A]. What's A? If you don't
care, you don't care. That's beautiful. And the compiler can even inference
some of it for you.

Now, if the only thing I have is a poor type system(like Java), then just give
me primitives and a struct.

~~~
JCoder58
You want F#.

> If I want to write a numeric value in a json output I'm creating that will
> be consumed by some other system, I want that thing to represent exactly
> that and spit an error if I ever try to assign something else, such as a
> string

[https://fsharpforfunandprofit.com/posts/serializating-
your-d...](https://fsharpforfunandprofit.com/posts/serializating-your-domain-
model/)

> I need to be able to say, "this number is not just any number, this is
> actually a temperature measurement in Celsius"

[https://docs.microsoft.com/en-us/dotnet/fsharp/language-
refe...](https://docs.microsoft.com/en-us/dotnet/fsharp/language-
reference/units-of-measure)

------
wyattpeak
I occasionally find myself interested in learning Haskell, wondering why I
haven't done so yet. So I go out and find an introductory tutorial, which is
without fail written with exactly the sort of smug self-satisfaction that this
thread is written. And I immediately lose interest.

Maybe the Haskell community is genuinely peopled by the most brilliant minds
ever to grace the planet, and the rest of us are out here farming shit. I'll
never know, because I'd rather struggle for a lifetime through poorly-
implemented languages than have to turn every time I have a problem to a
community completely unable to process the idea that they might be wrong.

~~~
naasking
> So I go out and find an introductory tutorial, which is without fail written
> with exactly the sort of smug self-satisfaction that this thread is written.
> And I immediately lose interest.

I think reading that tone into a tutorial, or from this thread, says more
about you than the community.

And even if they were smug, consider the possibility that maybe they have good
reason. Would you never buy a Tesla because Elon Musk smugly claims his cars
are the best on the market?

> I'll never know, because I'd rather struggle for a lifetime through poorly-
> implemented languages than have to turn every time I have a problem to a
> community completely unable to process the idea that they might be wrong.

Cut off your hand to spite your face? Your choice of course, but from my
experience as a non-Haskeller, the community is perfectly welcoming and
willing to explain details to beginners.

Also, if the Haskell "community" never thought they were wrong, they would
never disagree. That's clearly not the case if you perused any mailing list or
thread.

~~~
wyattpeak
TFA:

> which really should have implemented a well-known concept (Monoid, Monad,
> Alternative, whatever), but they fell short because they (probably) didn't
> know the concept

If that's not smug self-satisfaction, I don't know what is. No consideration
that there were reasons they implemented their system in a different way.
Suggestion that it was done due to ill-education on their part. I reread it
carefully after your accusation. I don't want to too readily assume the worst
in people. I am extremely comfortable with the accusation I laid on that
thread.

But to your other point, I don't consider the possibility that they have good
reason for their smugness. I don't find it implausible (as I said, albeit
sarcastically), I just find smugness entirely orthogonal to correctness. There
are brilliant smug people, but flat-earthers are also among the more smug
people I've encountered.

If I lived in a world with only ten languages, I might agree I was punishing
myself more than them. But I will never learn every language, tool, technique
or skill I want to, so why would I ever start here?

~~~
naasking
> If that's not smug self-satisfaction, I don't know what is. No consideration
> that there were reasons they implemented their system in a different way.

Of course there was consideration. The history surrounding this is well
documented. People familiar with the relevant concepts raised the problems
with the JS Promise API early on, but were dismissed.

Which is also beside the point, because generalizing from N=1 is silly. If I'm
being generous, you pointed out one smug Haskeller, where you claimed that the
entire community was smug.

> If I lived in a world with only ten languages, I might agree I was punishing
> myself more than them. But I will never learn every language, tool,
> technique or skill I want to, so why would I ever start here?

Allow me to rephrase with an analogy to illustrate: I can easily solve 90% of
my problems perfectly well with just integers, reals and complex numbers, so
why would I ever learn general vector or matrix math?

Certainly that's your perogative, but I hope you'll agree that you're ignoring
a powerful tool that would subsume all of your previous understanding of these
abstractions, and which would let you solve those same problems, and more,
_better_.

------
js8
I think that classes (in the school of OOP coming from C++ and Java) are
incorrect abstractions. Here incorrect is in the eye of the beholder, but I
would argue that tying the three fundamental principles of OOP (encapsulation,
inheritance, polymorphism) into one abstraction leads to more problems than it
solves.

In particular in Haskell, these things are separated into modules
(encapsulation mechanism), abstract data types (inheritance mechanism) and
type classes (polymorphism mechanism). But other modern languages have this
separation as well, and it is cleaner.

Consider for example a common Java exam question, should you use interface or
abstract class? This is a completely artificial problem that doesn't occur in
languages that separate these abstractions orthogonally. Also another common
exam question, access control (private, public, protected) is more complicated
due to lack of the separation of encapsulation and inheritance principles.

Another example is Java is arguably much less readable than it could be
because organizing all the code as classes makes code more difficult to follow
(especially without IDE it's insanity of directories) - you don't know which
classes are just utility types or simple functions and which actually contain
the meat of the code. A properly organized module is much more pleasant to
work with.

~~~
legerdemain
In Java, it is perfectly acceptable to have either a file containing the
primary public class and any number of private utility and implementation
classes, or just have a non-instantiatable enclosing class that works as a
module and a bunch of interfaces, classes, and enums within it. Consumers then
can then refer to them either by their Class.SubClass name or the shorter
SubClass name.

~~~
js8
OK, thanks! I didn't realize it is possible, I haven't seen code that uses
this technique.

~~~
watwut
Typically nested classes in Java make it harder to follow what each class
does, where it ends and starts. The files then tend to become too big and at
worst end up cross referencing each others privates too much. At least, that
is my experience with code that uses nested classes all the time and - I am
slowly refactoring it to split classes into separate files.

------
crimsonalucard1
Don't forget about Go:

    
    
        func Open(name string) (file *File, err error)
    

Your basic Optional/Maybe monad would have worked better here.

There is a lot of evidence supporting the fact that Rob Pike and company when
inventing Go were not aware of Sum types indicating that this special Tuple
return type (that can never be reused anywhere and must be pattern matched
immediately after it returns) was created because of lack of knowledge and not
as a design choice.

See this thread as evidence:
[https://news.ycombinator.com/item?id=6821389](https://news.ycombinator.com/item?id=6821389)

~~~
nsajko
Where did you learn (or, more generally, where do people learn) about those
Sum types?

The Wikipedia page for "Sum type" redirects to "Tagged union", implying those
are synonyms; do you agree with that?

Sadly, that Wikipedia page only seems to refer to pages specific to some
programming languages.

Could you maybe recommend an introductory book about type theory, preferably
with an orientation to how it can be applied to language design? EDIT: or
maybe a programming language theory book? To be honest, I am mainly interested
in imperative languages, basically how could a better Go have been made.

~~~
crimsonalucard1
Languages with Algebraic Data Types will help you get practical knowledge on
how to use these types. Basically types share an isomorphism with algebra
allowing certain types to share properties with algebra, hence the name sum
types and product types.

Bool has a cardinality of two. True and False. A Product type (essentially a
tuple) of two bools represents a bool and another bool and has a cardinality
of 4. (True, True), (True, False), (False, False), (False, True). The
cardinality of a product type is essentially the product 2 * 2 of the
cardinality of both individual types. Product types represent the concept of
AND: a bool AND another bool. The product type in itself is a type evaluated
as the product of two (or more) types. In most languages this concept is
represented as a struct or a tuple.

To remove confusion for sum types let us create a type with a cardinality of
4. FuzzyBool. It has 4 instances: VeryTrue, SortaTrue, SortaFalse, VeryFalse.
A sum type represents the concept of OR. Let's see a sum type of a Bool OR a
FuzzyBool (Bool + FuzzyBool). The new type can either be one of: (True | False
| SortaTrue | SortaFalse | VeryTrue | VeryFalse). The cardinality is 4 + 2.
Hence the term sum type.

Languages are typically asymmetric in types due to the lack of the explicitly
offering the user the ability to combine types to form a sum type.

The Golang return value for functions is actually the opposite of a sum type.
It is a product type. Rob Pike was likely unaware that you can create a type
that is either (File* or Error) and instead utilized ((File* | null) && (Error
| null)) to attempt to keep error handling within the type system. It results
in IO functions having a return type with Cardinality of greater than 4: (File
_, nil), (File_ , err), (nil, nil), (nil, err) and they just expect you to
ignore (File*, err) and (nil, nil) and assume it will never occur even though
the type system does not restrict it from occurring.

If you want to learn this stuff imperatively I would recommend Rust, however
you could miss the big picture with Rust due to the borrow checker and the
option to do typical C-style programming that avoids utilizing the ADT's to
the full extent of their power.

I would recommend diving into something like Haskell and getting to know
algebraic data types and exhaustive pattern matching. You can avoid fully
understanding Monads if all you want to understand is Sum types. Haskell will
force you to utilize sum types very early on via exhaustive pattern matching.

------
noelwelsh
My favourite example, because I've been working with it recently, is the
nunjucks templating language. It has three or four different ways of almost-
but-not-quite implementing functions. It has dynamic scoping (hello, 1960s!)
But my favourite feature is its recursive for-loops. These are truly majestic.
I would not believe someone could implement a language so broken if I had not
witnessed it.

------
saagarjha
Good link:
[https://old.reddit.com/r/haskell/comments/glz389/examples_of...](https://old.reddit.com/r/haskell/comments/glz389/examples_of_incorrect_abstractions_in_other/)

------
barbarbar
This reminds me stdout's fine tune: My language is better than your language.
It captures a lot of what this thread is about.

------
bjoli
I never understood the complaint about generators. Languages can have both
light-weight laziness (like generators) and more heavy abstractions (like
scheme's srfi-41). The overhead of generators in, say, JavaScript is minimal.
If you want odd lazy lists, generators is a great thing to use to build it.
Generators and streams (as in scheme's SRFI 41) are not mutually exclusive.

------
millstone
If Unicode were a monad, Haskell would have gotten String right.

------
bedobi
Null and exceptions. (in Haskell, Maybe and Try/Either)

~~~
dmix
Nothing beats pattern matching on Maybe for exception or null handling, or
variations of such (Rust's Option). It's just so clean syntactically.

~~~
mirekrusin
I’d argue that nullable type with refinements as in flow for example beats
Maybe most of the time. You can’t make it simpler (one letter ? overhead)
without special unwrapping constructs (refinements) just normal code with same
guarantees.

~~~
lmm
Refinement typing is too complicated IME. Haskell guarantees that you can
always pull out an expression into a function without changing anything, and
vice versa, whereas with refinement types you can extract a function and find
your code no longer compiles.

------
donatj
I was thinking just yesterday while casting an int32 to a uint64 that theres
no guarantee the int32 wasn’t negative beyond my knowledge that the code was
only ever incrementing it, and how most languages (that I have worked with at
least) allow dangerous casting without enforcing any sort of error handling on
the given operations.

I think this would fall into this category.

~~~
japanuspus
You should definitely have a go at rust where this conversion would have to be
done explicitly via `try_from` [0] which may fail.

[0]: [https://doc.rust-
lang.org/beta/std/convert/trait.TryFrom.htm...](https://doc.rust-
lang.org/beta/std/convert/trait.TryFrom.html)

------
toolslive
This classic contains a lot of them:
[https://www.destroyallsoftware.com/talks/wat](https://www.destroyallsoftware.com/talks/wat)

------
D13Fd
It's threads like this that remind me how little I know...

~~~
mantap
A lot of Haskell jargon is just simple everyday concepts dressed up with
complicated neologisms.

~~~
antonvs
Could you give an example?

~~~
pas
Monad. It's just a very generic container. You can put things into it
(unit/pure/return/bind) and you can do things within it (flatmap). Sometimes
it's an effect or control flow container (Try, Future, Result, Either, Maybe,
Option), sometimes it's just a List.

And then there's cata (catamorphism), which is the generic
reduce/fold/accumulate/aggregate.

And then there's bifunctor, which is the general Either, Result, or
List[(A,B)] thingie.

These concepts are very useful, but usually typed FP code nowadays are full of
compromises in the name of typing. Typed FP code is constrained by what the
type system allows.

This eventually boils down to everything becoming for-comprehensions / do-
notations. Which is fine after you get the hang of it. But it's very easy to
run into real world problems that require "hacks" or inelegant solutions, or
forces the correct solution, which is very time consuming.

~~~
antonvs
Monad has a specific set of mathematical rules, though, which have important
properties which make them well-suited for some very specific tasks, including
defining the semantics of programming languages.

I'm not aware of any "simple everyday concept", or anything in any mainstream
programming language, that comes close to being equivalent to what monads are.
You can find examples of specific monads being used (and misused) in many
ordinary programs, because of their deep connection to programming language
design, but they're never explicitly identified as such, partly because most
languages can't conveniently make use of an abstraction like that.

Similarly, there's more to catamorphisms than what you describe. What you
described is essentially what the generic mathematical concept of a
catamorphism looks like in a specific context, but that's like saying that the
term "vehicle" is just the simple everyday concept of a bicycle dressed up
with a complicated neologism.

Something similar goes for bifunctor. `Either` is an example of a bifunctor,
but it's also an example of a functor. Conflating the generic concept with
specific instances will mislead you.

The point about this terminology, and the concepts it represents, is that it
classifies these abstractions with precise rules, allowing the commonality
across many different abstractions to be identified and exploited in well-
defined, rigorous ways.

You don't find that in mainstream programming languages or the literature
about them. The closest you'll come is things like object-oriented collection
class hierarchies - Collection / List / Set / OrderedSet etc. That kind of
classification is analogous to the sort of terms we're discussing, but is
nowhere near as well-factored or mathematically rigorous.

> This eventually boils down to everything becoming for-comprehensions / do-
> notations.

What language are you thinking of? Sounds like Scala maybe. Unfortunately I
think Scala was a mistake from the point of view of introducing functional
programming to a wider audience. The object/functional mixture results in a
kind of worst of both worlds that doesn't show off the benefits of either to
best effect.

> usually typed FP code nowadays are full of compromises in the name of
> typing.

> But it's very easy to run into real world problems that require "hacks" or
> inelegant solutions, or forces the correct solution, which is very time
> consuming.

Perhaps you've had this experience from experimenting with functional
languages, but it's not my experience from actually using languages like
Haskell and OCaml in real projects for many years. I'm also very experienced
with many mainstream languages - Java/Javascript/C/C++/Python etc. - and I
find functional languages to be models of elegance by comparison. Statically
debugging code by designing the types well ("make illegal states
unrepresentable") is a huge timesaver when it comes to avoiding bugs and
making significant changes to large codebases easier.

There's a lot of research on extending the power of type systems, which it
seems like you may be thinking of, but for most ordinary purposes, you don't
need to worry about that.

~~~
pas
Of course, Haskell is right, the jargon is precise. That doesn't mean
underlying concepts are not what everyday programmers use. The jargon has
given proper names to the general concepts, it reified them, made them first-
class citizens.

I'm not saying they are not useful. (On the contrary, they are invaluable for
high level discussions about certain aspects of programming, including control
flow, data structures, and programming techniques.)

Every async/await implementation is basically a monad with fancy syntax.

> Conflating the generic concept with specific instances will mislead you.

Yeah, agreed, no questions there. I'm just saying that the vast majority of
programmers are nowhere near prudent enough to appreciate the strength of a
correct/lawful system, plus they usually have other powerful concepts in their
heads (after all there are many C programs that are sort of correct but not
easily written with standard, total, and sound types, plus the required
assumptions are not expressed in the source code). Also, another angle I think
might help to convey what I'm trying to say is to look at how much jargon is
needed to explain things like for-comprehensions execute the monad steps in
sequence, but if you use mapN and an applicative then you can execute
independent steps in paralell. Whereas it's just "race the futures" (or
Promise.all, or whatever) in common programming parlance.

> Perhaps you've had this experience from experimenting with functional
> languages [...] > What language are you thinking of? Sounds like Scala
> maybe.

Yes, I don't know Haskell, I have a few years of Scala with some Scalaz
experience. And maybe you're familiar with mypy and TypeScript, and it's
obvious how problematic is to build the bridge between existing semantics and
a correct/sound type system. (And of course, it's very much impossible,
because you either have soundness or not, and so TS is unsound to be useful,
and mypy is useless unless you rewrite many things.)

> [...] and I find functional languages to be models of elegance by comparison

I haven'got the hang of any pure functional language (yet?). I like to think
it's not because of lack of trying. I see where concepts can be useful (eg.
how TS and Rust would benefit from HKT, and how many people are waiting for a
more expressive type system to create better abstractions, better libraries,
better APIs), but to me all Haskell code looks like something like a dadaist
poem with strange symbols sprinkled here and there.

(Eg I'm looking at the sample app for miso
[https://github.com/dmjio/miso#sample-
application](https://github.com/dmjio/miso#sample-application) and I have no
idea what is what, no idea about shapes, what's <#, what's >>, why div_ and
button_ have underscores after them, why div_ takes an empty list, what's
{..}? And sure, most of these can be found, but still a lot harder than even
the most damned FactoryFactoryBean in Java.

And sure, I appreciate that lenses and the Transition interface helps with
stuff, but that's simply a very small incremental ergonomic enhancment
compared to what I wish.

Similarly, when I look at servant's documentation I wonder how come a such
powerful language, but to model 'GET /user/:userid' it can't - or doesn't -
use /user/:userid, but "user" :> Capture "userid" Integer. The type is nice,
and whatever :> is, I'm sure that's nice too, but I'm not finding the elegance
that TSed has - [https://github.com/TypedProject/tsed#create-your-first-
contr...](https://github.com/TypedProject/tsed#create-your-first-controller) )

So, all in all, I'm just a lazy party-pooper when it comes to Haskell (or FP
in general). And mostly just because the pain of debugging TypeScript and the
myriad "any"-s everywhere still seems the lesser one than trying to grok the
the cryptic symbols.

~~~
antonvs
> I'm just saying that the vast majority of programmers are nowhere near
> prudent enough to appreciate the strength of a correct/lawful system

That seems like a failure of education. Imagine if we put up with bridges that
collapsed because "most engineers are nowhere near prudent enough..."

People use what they were taught to use. Programming languages form part of
that teaching. Bad programming languages teach people bad habits.

> after all there are many C programs that are sort of correct but not easily
> written with standard, total, and sound types

For example? I suspect there's a kind of all-or-nothing mentality behind this.
I would bet you can easily write the sort of programs you're thinking of in a
good functional language, if you don't get hung up on perfection. It would
still be a significant improvement. There are many examples of this - XMonad
comes to mind, which you can compare to i3 which is written in C.

It seems to me that most of what you're saying boils down to simple
unfamiliarity. I would suggest that you're suffering from a kind of inverse
expert blindness. You discount the amount of learning that went into your
ability to understand a phrase like "race the futures," and this leads you to
reject systems that are different enough that you can't easily apply that
existing expertise to.

Your difficulties in reading miso code without, apparently, consulting _any_
documentation is a little silly, I'm afraid. What programming language, that
is significantly different from the ones you already know, allow you to read
its code without any background knowledge?

If you're actually working with the language, there are easy ways to answer
all the questions you're asking. Short of _gasp_ reading the docs, you can
query the functions or operators you're interested in at the ghci prompt. For
example:

    
    
        > :t div_
        div_ :: [Attribute action] -> [View action] -> View action
    

This helpfully points out that the first argument to `div` is a list of
Attributes. If there are no attributes, that list would be empty.

That wasn't so hard!

You could also read the docs:
[https://hackage.haskell.org/package/miso-1.5.2.0/docs/Miso-H...](https://hackage.haskell.org/package/miso-1.5.2.0/docs/Miso-
Html-Element.html#v:div_)

Or look it up in Hoogle:
[https://hoogle.haskell.org/?=&hoogle=div_%20package%3Amiso](https://hoogle.haskell.org/?=&hoogle=div_%20package%3Amiso)

> Similarly, when I look at servant's documentation I wonder how come a such
> powerful language, but to model 'GET /user/:userid' it can't - or doesn't -
> use /user/:userid, but "user" :> Capture "userid" Integer

It's expressing the route in the language rather than in an untyped string. :>
is just the operator servant defines to separate route components. But here
(and in some of your other observations) you seem to be conflating a decision
that the servant authors made, with the language itself.

> So, all in all, I'm just a lazy ...

That's your choice. You should just know that your critiques are off the mark
- they're the stories you're telling yourself to justify not having to learn.

~~~
pas
Thanks for responding! I really wasn't hoping you'd think my rambling is worth
responding to.

> Imagine if we put up with bridges that collapsed because "most engineers are
> nowhere near prudent enough..."

Most developers don't develop nuclear reactor control software.

And I'm saying this, not because your statement about education is wrong, but
because there's a cost-benefit analysis going on, and traditionally
correctness was too expensive. This is due to insufficient tools, a very
strange mindset ("That seems like a failure of education."), plus the path-
dependence of the real world (the whole history of computer programming
somehow led to a lot more close-to-the-metal imperative code and programming
languages than "here are the correct abstractions and let's see how far can we
go with them" ones). Also, as far as I know we need dependent types to be able
to easily model a lot of invariants that would help with many kinds of
programming errors. (Of course validating safely composable components would
go a long way - like that project targeting some subset of Rust, but that's
basically what static analysis is. And there's a whole industry that tries to
bolt-on this kind of correctness verification after the code has been
written.)

...

All in all, I'm simply pointing out that the Haskell ecosystem is still not
exactly approachable. You say it's because education, and people's choices,
and other reasons. (Which is tautologically true.) But that doesn't help much.
In my experience programmers get better when tools get better, and it makes
sense to learn them, when it gives them sufficient value. (Correctness,
productivity, ergonomics, performance, hipness and whatnot, and all of that on
a reasonable time-horizon.)

This whole comment thread started out from talking about jargon, and about the
value of having mathematically exact, precise (correct) concepts as
definitions used for everyday programming, and my observation is that
programmers and programming as an occupation don't have the need/want for such
high level concepts and correctness, and on top of that, even if everyone
wants better programming tools, somehow the obvious answer isn't rewrite it in
Haskell. Maybe the gap is [stil] too big [in the average case]. Maybe the very
strong static typing, and pure FP (and the basically necessary immutability)
and jargon, and unfamiliarity is still too constraining, too "expensive" to
learn and use.

