[HN Gopher] I learned Haskell in just 15 years
___________________________________________________________________
I learned Haskell in just 15 years
Author : aranchelk
Score : 258 points
Date : 2024-06-17 03:44 UTC (19 hours ago)
(HTM) web link (duckrabbit.tech)
(TXT) w3m dump (duckrabbit.tech)
| munchler wrote:
| Cute. All kidding aside, though, functional programming is worth
| the effort to learn, and it doesn't actually take 15 years. The
| payoff is at the end of the article:
|
| "It's quite natural to program in Haskell by building a
| declarative model of your domain data, writing pure functions
| over that data, and interacting with the real world at the
| program's boundaries. That's my favorite way to work, Haskell or
| not."
|
| Haskell can be intimidating, though, so I would recommend F# for
| most beginners. It supports OOP and doesn't require every single
| function to be pure, so the learning curve is less intense, but
| you end up absorbing the same lesson as above.
| initplus wrote:
| Yes - the value of functional programming isn't that working in
| OCAML, or F#, or Haskell is 10x as productive as other
| languages. But that it can teach you worthwhile lessens about
| designing software that apply equally to imperative languages.
|
| Modelling the business domain, reasoning and managing side
| effects, avoiding common imperative bugs, these are all
| valuable skills to develop.
|
| F# is a great language to learn, and very approachable. Worst
| part about it is interacting with antiquated .NET API's. (I
| can't believe the state that .NET support for common
| serialization formats is still in...)
| sidkshatriya wrote:
| > Yes - the value of functional programming isn't that
| working in OCAML, or F#, or Haskell is 10x as productive as
| other languages.
|
| This is not true in my personal experience.
|
| As has been famously said (paraphrased): Functional
| programming makes tough problems easy and easy problems
| tough.
|
| In other words the value of functional programming depends on
| your domain.
| NinoScript wrote:
| So you're saying that it does make you 10x as productive?
| initplus wrote:
| Maybe my phrasing is not clear - I meant that these
| languages are indeed not significantly more productive.
| marcosdumay wrote:
| But (and I agree with the GP) they are. They are
| overwhelmingly more productive, in a way that you often
| can't even compare quantitatively.
|
| They are also a lot less productive. It depends entirely
| on what you are doing.
| grumpyprole wrote:
| By what measure? Haskell can be a huge productivity
| multiplier. The standard library is built upon many
| powerful, unifying and consistent mathematical
| abstractions. For example, there is almost no boilerplate
| to write for any traversal, mapping, error handling etc.
| The average Pythonista simply has no idea what they are
| missing. But Haskell doesn't have anywhere near the third
| party ecosystem of Python, so is less productive by some
| measures.
| antonvs wrote:
| > easy problems tough.
|
| That needs a qualifier: it can make easy problems tough if
| you're not familiar with how to solve them in a functional
| context.
|
| A big part of that is because smart people have already
| solved the tough problems and made them available as
| language features or libraries.
| jiggawatts wrote:
| Absolutely! Any beginner can readily combine the
| catamorphisms and anamorphisms in `recursion-schemes`, or
| use the ready-made hylomorphisms for common tasks such as
| setting a value in a data structure. What could be
| simpler? /s
|
| https://wiki.haskell.org/Zygohistomorphic_prepromorphisms
| amoss wrote:
| They have played us for absolute fools.
| cubefox wrote:
| > That needs a qualifier: it can make easy problems tough
| if you're not familiar with how to solve them in a
| functional context.
|
| All problems are easy if you are familiar with how to
| solve them. Unfortunately it's part of the problem to
| find out how to solve them, and that can be unusually
| hard in case of functional programming. Like solving
| something with recursion instead of loops + states. There
| is a reason cookbooks use loops not recursion.
| simiones wrote:
| Not really, certain problems are just inherently harder
| to express in a purely functional way than they are in an
| imperative way (and the reverse is just as true). For
| example, computing a histogram is much simpler in
| imperative terms (keep an array of histogram values, go
| through the original list, add 1 to the array element
| corresponding to the current element in this list) than
| in a purely functional style, especially if you need a
| somewhat efficient implementation.
|
| My favorite example of this is implementing quicksort.
| It's significantly easier in C than it is in Haskell.
| chuckadams wrote:
| > My favorite example of this is implementing quicksort.
| It's significantly easier in C than it is in Haskell.
|
| Oh please, what's so hard about qsort
| :: Ord a => [a] -> [a] qsort [] = []
| qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
| where lesser = filter (< p) xs
| greater = filter (>= p) xs
|
| :)
|
| folks, take that with a big ol /s, you would never want
| to actually use that algorithm. But the real deal isn't
| all _that_ awful:
| https://mmhaskell.com/blog/2019/5/13/quicksort-with-
| haskell
| simiones wrote:
| Well, I'd say using an entirely different collection type
| than the rest of language (STArray instead of [a]) is
| already a big complication. It also ends up being more
| than double the size of the Java code. And, as the author
| admits, it's actually even slower than the original not-
| quicksort implementation above, because it actually has
| to make a copy of the original list, and then return a
| copy of the mutated array.
|
| So, one of the best sorting algorithms ever devised is
| not actually usable to sort a [a] in Haskell... I
| maintain this is a good example of making an easy problem
| tough.
| tome wrote:
| > I'd say using an entirely different collection type
| than the rest of language (STArray instead of [a]) is
| already a big complication
|
| Haskell uses many different collection types, just like
| any other language. Why not?
|
| > it's actually even slower than the original not-
| quicksort implementation above, because it actually has
| to make a copy of the original list, and then return a
| copy of the mutated array.
|
| Sure, but it could also not do that, if callers are happy
| to provide a mutable array, just like any other language
| ...
|
| > one of the best sorting algorithms ever devised is not
| actually usable to sort a [a] in Haskell
|
| Indeed! One of the best algorithms for sorting a mutable
| array can't be used on an immutable data type, just like
| any other language ...
|
| None of this invalidates your original claim that "It's
| significantly easier in C than it is in Haskell" of
| course.
| chuckadams wrote:
| quicksort2 has a very reasonable constraint of Array,
| it's the helpers that use the STArray implementation. I
| suspect it wouldn't be hard to port to MArray, though I
| don't know that it would perform any better (maybe
| avoiding some copies since MArray is actually usable
| outside of runST). I also suspect the overhead of the
| copies pays off with larger lists given the lack of space
| leaks compared to the naive algorithm. Some benchmarks of
| different-sized lists would have been nice.
|
| I'm not a cheerleader for Haskell, it's not the most
| appropriate language for every job (certainly not for
| quicksort). But some folks suddenly become hyper-
| optimizing assembly programmers whenever anyone has the
| thought of porting a sql crud app to another language...
| Horses for courses and all that.
| hnfong wrote:
| Arguably the issue with "quicksort in Haskell" is not
| that it's "hard" to implement, but rather it defeats the
| whole purpose of using a "purely functional" language.
|
| The pragmatic way to look at Haskell is not that it's
| purely functional, but rather, you could write
| <del>imperative code</del> Monads if you wanted, and that
| gives a "functional by default" environment, whereas most
| imperative languages default to mutable objects etc that
| are not friendly to functional-style programming.
|
| But then the more modern languages are catching on with
| immutable by default variables etc. so in the end the
| differences between newer languages may not be that great
| after all...
| trealira wrote:
| I know you're being sarcastic, but even in this linked-
| list quicksort, it could be made more efficient. By
| defining lesser and greater separately, you're traversing
| the list twice. You could compute them at the same time
| by changing those last two lines to this:
| where (lesser, greater) = partition (< p) xs
|
| I just learned about the existence of this function
| today, and wonder why I had never seen it used before.
|
| https://hackage.haskell.org/package/base-4.20.0.1/docs/Da
| ta-...
| chuckadams wrote:
| Heh, I've used some version of partition in my TS/JS code
| ever since underscore.js. But "Haskell Quicksort" is
| basically a code meme now, and I didn't think to optimize
| it (though I think the original was a one-liner using
| list comprehensions)
| kreyenborgi wrote:
| > makes tough problems easy and easy problems tough
|
| And because of mutual recursion, that means that tough is
| easy (and easy tough). In other words, if we call the class
| of tough problems T and easy problems NT, we have T==NT,
| given FP.
| williamcotton wrote:
| What easy problems are tough in F#? I've been using it for
| writing random scripts and as a Python replacement.
| z500 wrote:
| Writing recursive descent parsers in F# is a lot of fun
| with ADTs and pattern matching.
| chrischen wrote:
| It's only tough to change your way of thinking. Most people
| making the switch find it tough because they are trying to
| find imperative techniques to do something in a functional
| way and struggling because they can't find an if else
| statement or a for loop. But if you were never taught to
| think in terms of conditional branching or looping indexes
| you'll save a lot of time.
| pyuser583 wrote:
| How do you "Hello World" in a functional language? Doesn't
| it have side effects?
| sevensor wrote:
| There's some real confusion about what "functional"
| means, because it depends on who's speaking. Writing
| _exclusively_ pure functions is a Haskell thing. A much
| looser definition of the functional style would be to say
| that you mostly write pure functions over immutable data,
| but when you have to actually do a side effect you write
| a procedure that talks to the outside world and go on
| with your day. If you go digging in this site's archives
| from about ten years ago, I recall numerous debates about
| what constituted functional programming, and whether the
| latter counts at all. But if we _are_ talking about
| Haskell, the answer to your question is obviously
| "Monads."
| cess11 wrote:
| The string "Hello World" evaluates to itself, what else
| do you need?
|
| Edit: Eh, I thought it was a fun quip.
| pyuser583 wrote:
| I laughed
| NateEag wrote:
| Never forget PHP's "Hello World":
| Hello, world!
| trealira wrote:
| Yes, and AFAIK, you're pretty much free to cause side-
| effects in functional languages; it's just a bit awkward
| and somewhat discouraged. It's kind of like how C still
| has goto, yet it's still a structured programming
| language.
|
| Even in Haskell, which tries to control side-effects
| more, it's not hard; it's just that it's stuck with an
| "IO" annotation. Any function that calls an IO function
| also becomes an IO function; it's infectious.
| main :: IO () main = putStrLn "hello, world"
| dllthomas wrote:
| It has an effect. Whether it's a "side effect" depends on
| how we've defined that.
|
| One way of viewing Haskell is that you are lazily
| constructing the single composite "effect on the world"
| called main. helloWorld :: IO ()
|
| then is a value representing an effect, but it only
| actually happens when it becomes a part of main.
|
| Threads complicate this but don't completely destroy the
| model.
| wruza wrote:
| Hot take of the day: you learn that with imperative
| programming just as well.
|
| I familiarized myself with fp to the point of writing scheme
| and haskell around 15 years ago. Read the classics,
| understood advanced typing, lambda calculus and so on. The
| best "fp" I'm using nowadays is closures, currying in the
| form of func.bind(this[, first]) and map/filter. Which all
| are absolutely learnable by the means of closures, which are
| useful but I can live without. Sometimes not having these
| makes you write effing code instead of fiddling with its
| forms for hours.
|
| Still waiting for the returns from arcane fp-like code I
| produced earlier. Cannot recognize nor understand none of my
| projects in this style that I bothered to save in vcs.
| Imperative code reads like prose, I have some of it still in
| production since 2010.
|
| These FP talks are disguised elitism imo (not necessarily bad
| faith). Beta reduction and monadic transformers sound so
| cool, but that's it job-wise.
| cubefox wrote:
| > These FP talks are disguised elitism imo (not necessarily
| bad faith). Beta reduction and monadic transformers sound
| so cool, but that's it job-wise.
|
| They may be disguised mathematics. People are into math
| because it is neat / elegant / cool. So they study it
| regardless of whether it has a practical use or not.
| photonthug wrote:
| Some programmers have serious math envy. This can be good
| if they are self aware about it and keep it in check,
| because it makes them better programmers. Otherwise they
| can a pain to work with. Seniors should be people that
| have dealt with this aspect of their own talent, not
| juniors who are promoted in spite of or because of it
| dboreham wrote:
| Mathematics is just a kind of programming. And vice
| versa.
| cubefox wrote:
| Programs fundamentally have state, while mathematical
| equations have not. Math is in its core declarative,
| while programming is essentially imperative, at least
| under the hood.
| jerf wrote:
| In theory, you could pick up your one language, say, Java,
| and through the course of a normal career learn everything
| necessary to program in that language in the best possible
| way.
|
| In practice, it's a pretty well-known phenomenon
| experienced by many skilled programmers that being _forced_
| into different styles by different languages results in
| learning things that you would only have learned very
| slowly if you had stuck only to your original language. To
| be concrete about the "very slowly", I'm talking time
| frames of your entire career, if not your entire life and
| beyond. It would be a long time programming in Java before
| you discover the concept of something like "pure functions"
| as a distinct sort of function, a desirable sort of
| function, and one that you might want organize your
| programming style around.
|
| Of course, having heard of the concept already, we'd all
| like to fancy ourselves smart enough to figure it out in
| less than, say, three decades. But we're just lying to
| ourselves when we do that. Even the smartest of us is not
| as smart as all of us. You are not independently capable of
| rediscovering everything all the various programming
| communities have discovered over decades. If you want to
| know what even the smartest of us can do on their own
| without reference to decades of experience of others, you
| can look into the state of computer programming in more-or-
| less the 1980s, 90s if you're feeling generous. I think
| we've learned a lot since then, and the delta between the
| modern programmer and a 1980s programmer certainly isn't in
| their IQ or anything similar, it is in their increased
| collective community experience.
|
| By getting out into systems that _force_ us to learn
| different paradigms, and into communities that have learned
| how to use them, we draw on the experience of others and
| cover far more ground than we could ever have covered on
| our own, or in the context of a single language where we
| can settle into a local optima comfort zone. Jumping out of
| your original community is kind of an annealing process for
| our programming skills.
|
| "The best "fp" I'm using nowadays is closures, currying in
| the form of func.bind(this[, first]) and map/filter."
|
| That is really not the lesson about software design that FP
| teaches, and blindly carrying those principles into
| imperative programming is at times a quite negative value,
| as your experience bears out. FP has more to say about
| purity of functions, the utility of composition of small
| parts, the flexibility of composition with small parts,
| ways to wrap parts of the program that can't be handled
| that way, and providing an existence proof that despite
| what an imperative programmer might think it _is_ in fact
| possible to program this way at a system architecture
| level. I actually agree 100% that anyone whose takeaway
| from FP is "we should use map everywhere because they're
| better than for loops and anyone who uses for loops is a
| Bad Programmer" missed the forest for the leaves, and I
| choose that modification of the standard metaphor
| carefully. I consider my programming style highly
| influenced by my time in functional programming land and
| you'd need to do a very careful search to find a "map" in
| my code. That's not what it's about. I'm not surprised when
| imperative code is messed up by translating that into it.
| wruza wrote:
| That might be, probably is. But the representation of FP
| gets mostly done by those who are only halfway there,
| creating an impression that it is a better way of
| programming overall, when it's just a mixed bag of
| approaches dictated by a set of esoteric languages (from
| business pov). The worst part is that it doesn't
| translate verbatim to any non-fringe language and creates
| a mess in it, due to adopted inertia. At least that is my
| experience with FP "recruitment".
|
| I wish I skipped this FP tour completely and instead
| learned how to structure my programs directly. Could save
| me a year or five. Maybe there's no better way, but in
| practice clear explanations are always better than these
| arcane teachings where you repeat something until you get
| it by yourself.
| cess11 wrote:
| "In theory, you could pick up your one language, say,
| Java, and through the course of a normal career learn
| everything necessary to program in that language in the
| best possible way."
|
| OK, then you know about currying, immutable data
| structures, map/reduce/filter, &c.
|
| Because Java has that since way back when. No real
| closures, I think, but that doesn't matter much because
| the anonymous functions do what you want pretty much all
| the time and you could probably invent your own closures
| if you really want something else.
| SkyMarshal wrote:
| _> OK, then you know about currying, immutable data
| structures, map /reduce/filter, &c._
|
| It's not a certainty you learn about those things from
| Java, depends on your team/manager/codebase. None of that
| is enforced in Java the way it is in fp. Plus, none of it
| is really core or native to Java, it was added on later.
|
| That's how we got essays back in the 2000s like _" The
| Perils of Java Schools"_, _" Can Your Language Do This"_,
| and _" Beating the Averages"_.
|
| https://www.joelonsoftware.com/2005/12/29/the-perils-of-
| java...
|
| https://www.joelonsoftware.com/2006/08/01/can-your-
| programmi...
|
| https://paulgraham.com/avg.html
| cess11 wrote:
| The constraint here is "the best possible way".
| SkyMarshal wrote:
| _> FP has more to say about purity of functions, the
| utility of composition of small parts, the flexibility of
| composition with small parts, ways to wrap parts of the
| program that can 't be handled that way, and providing an
| existence proof that despite what an imperative
| programmer might think it is in fact possible to program
| this way at a system architecture level._
|
| Adding to that, in my case it also made realize that
| deterministic elimination of entire classes of errors in
| large, complex code bases, in a systematic rather than
| ad-hoc way, is actually possible. Prior to discovering
| fp, and particularly Haskell's type system, I spent much
| effort trying to do that with a combination of TDD and
| increasingly elaborate try/catch/throw error handling.
| Discovering Haskell's compiler, type system, and monadic
| quarantining of effects obsoleted all that effort and was
| a huge eye opener for me. And a nice side-effect is easy,
| reliable refactor-ability. Being able to apply those
| concepts to imperative and other programming paradigms is
| where the real value in fp is, imho. Programmers still
| wrangling with the Tarpit [1] need to take a look if they
| haven't already.
|
| [1]:https://news.ycombinator.com/item?id=34954126
| cess11 wrote:
| I commonly implement things in an imperative style as a
| quick hack, then if it gets use I translate it into a more
| functional style. It kind of just happens as I clean it up
| and refactor during revisits.
|
| It might be a matter of taste, but I enjoy code built with
| functional abstractions that allow neat composable data
| flows and some caches loitering around. I find it also
| helps when adding UI. Sometimes performance could be better
| with mutation, but when I'm at that point I've already
| spent much more time tuning the thing with caches.
| baby wrote:
| Ocaml definitely doesn't make you more productive
| Bostonian wrote:
| I have not used OCaml, but presumably Jane Street thinks
| OCaml makes their coders more productive.
| nequo wrote:
| That's interesting because F#'s OOP, as someone who knows
| neither C# nor Java, makes it more intimidating to me than
| OCaml.
|
| Also interesting that when FP is mentioned, Hindley-Milner is
| implicitly understood to be part of FP too even though it
| doesn't have to be. Clojure emphasizes immutability and FP but
| with dynamic typing and everything that comes with that.
| mrkeen wrote:
| > Clojure emphasizes immutability
|
| Is "emphasizes" just another word for second-class support?
|
| C++ emphasizes the importance of memory safety.
| y1n0 wrote:
| Immutability is definitely first class in clojure, but you
| can work with mutable structures when you need to.
| mrkeen wrote:
| This is sounds like memory safety in C++.
|
| https://www.infoworld.com/article/3714401/c-plus-plus-
| creato...
| nequo wrote:
| This seems like a meaningless criticism when it comes to
| immutability in Clojure. You can have mutability in
| Haskell too. That doesn't make it as unsafe as memory
| management in C++.
| mrkeen wrote:
| > You can have mutability in Haskell too.
|
| Haskell enforces this via a type system.
|
| What _safe_ guards around mutability does Clojure have?
|
| If I import a method 'foo()', is there any kind of
| contract, notation ... anything which could suggest
| whether it mutates or not?
| eyelidlessness wrote:
| > What safeguards around mutability does Clojure have?
|
| Very nearly the entire language and standard library
| operate on immutable values only. Immutable values are
| the default, and you will use them for the vast majority
| of logic. You must do so, unless you very specifically
| opt to use dedicated reference types, at which point
| you'll still need to produce intermediate immutable
| values to interact with that vast majority of the
| standard library.
|
| And...
|
| > is there any kind of contract, notation ... anything
| which could suggest whether it mutates or not?
|
| Functions which mutate state are almost always suffixed
| !. They will typically fail if you pass them immutable
| values; they only operate on reference types, which have
| to be dereferenced (typically with the prefix @) to
| access their state.
| chipdart wrote:
| > Is "emphasizes" just another word for second-class
| support?
|
| I don't know what's your personal definition of "second-
| class support" but what it means is that it's explicitly
| supported by the language.
| mrkeen wrote:
| C++ explicitly _supports_ memory-safe programming. You
| can _choose_ whether you want to mess around with raw
| pointer arithmetic.
|
| What safeguards does the language actually put in-place?
| chipdart wrote:
| > C++ explicitly supports memory-safe programming. You
| can choose whether you want to mess around with raw
| pointer arithmetic.
|
| I don't think you know what you're talking about.
| Managing object ownership through systems like smart
| pointers is not memory safety. Applications that use
| smart pointers still suffer from memory issues, and it's
| possible to adopt object ownership systems that still use
| raw pointers, such as It's object ownership system.
| mrkeen wrote:
| > I don't think you know what you're talking about.
|
| Right. I sound just like someone talking about how "a
| language which emphasizes immutability" is an OK
| replacement for a language with pure functions.
| nequo wrote:
| The world is much less black and white than you'd like to
| see it.
|
| Functions in Haskell including Prelude can throw
| exceptions which is not reflected in the type signature
| of the function. That is an effect that makes seemingly
| pure functions impure.
|
| You can't judge a language from a list of buzzwords. You
| need to look at how it is used in practice.
| massysett wrote:
| > Functions in Haskell including Prelude can throw
| exceptions which is not reflected in the type signature
| of the function. That is an effect that makes seemingly
| pure functions impure.
|
| No, bottom, or _|_, is an inhabitant of every lifted
| type. An exception is bottom. So the / function is still
| pure even though it can throw a divide-by-zero exception.
| munchler wrote:
| Doesn't the "O" in OCaml stand for "Object", though? I think
| you could pick up either F# or OCaml just as easily.
|
| The nuances of OOP in F# can be ignored by beginners, so I
| really wouldn't let yourself be intimidated coming from
| Clojure.
|
| [0] https://ocaml.org/docs/objects
| armchairhacker wrote:
| OCaml classes and objects are (ironically) rarely used and
| generally discouraged. There are some cases where they're
| practically required, such as GUI and FFI (js_of_ocaml).
| But otherwise, most code does encapsulation and abstraction
| using modules and functor modules (which are more like
| Haskell and Rust typeclasses than traditional OOP classes).
|
| I don't know much about F#, but last time I used it most of
| its standard library was in C# and .NET, so F# code would
| interact with objects and classes a lot. AFAIK F# also
| doesn't have functor modules, so even without the
| dependence on C# code, you still can't avoid classes and
| objects like you can with OCaml (e.g. you can't write a
| generic collection module like `List` or `Set` without
| functors, it would have to be a collection of a specific
| type or a class).
| neonsunset wrote:
| F# uses .NET's generics, so the statement regarding
| List/Set is completely incorrect (all base collections
| are generic).
| penteract wrote:
| I think you misread their claim - they said that generic
| list/set would have to be classes, not modules (generic
| modules are a specific thing in OCaml and aren't the same
| as a module of generic classes).
| mrkeen wrote:
| F# has "generics" just like Python and PHP now "have
| types".
|
| It's not a yes/no feature.
| neonsunset wrote:
| Give F# a try. It has, and always had, true generics.
|
| https://learn.microsoft.com/en-us/dotnet/fsharp/language-
| ref...
| richrichie wrote:
| But why do we need Haskell for this?
| anon291 wrote:
| Realistically we don't but it's very rare to meet a
| programmer who understands these distinctions thats not also
| a great functional programmer.
|
| This is my experience after spending five years as a Haskell
| programmer and managing a Haskell team for several years and
| now moving back to the c++ world to play with AI.
|
| I know lots of good c++ programmers working on cutting edge
| stuff, real experts in their field, but they sometimes still
| don't have a clear way to understand how to model data
|
| That is my opinion. It's probably highly contentious.
| cornel_io wrote:
| I've actually had to fire a technically exceptional Haskell
| programmer because of the damage they did to our C#
| codebase (and arguably moreso, the team). Sometimes it's
| not a matter of talent or skill, but culture fit.
|
| In my experience FP-aligned people on non-FP projects tend
| to be more likely to overengineer, more prone to argue in
| favor of the Great Rewrite For No Reason Except Aesthetics,
| and more likely to abuse "lesser" programmers when they put
| up PRs. They suck as team players on teams that are not
| made of language nerds. I am _not_ just talking about the
| one person here who I fired, this is a legit pattern I 've
| noticed over at least a half dozen people.
|
| Conversely, they are exactly the right people to deploy
| when you have really tough, self-contained problems to
| solve that you wouldn't trust the normal Java 9-5ers to
| tackle.
|
| No matter how they do it, you can always rewrite their
| working code in a more maintainable language later once
| it's working, and make it integrate well with the rest of
| your stack. :D
| MrBuddyCasino wrote:
| This is my experience, too. Some of the worst code I've
| seen was a Haskell guy who first built his own
| (reactive?) concurrency framework and then implemented
| the actual functionality in completely unidiomatic and
| undocumented Java.
|
| Some people don't understand that the ,,best solution" is
| not necessarily equal to the most beautiful abstraction
| they can think of.
| auggierose wrote:
| > Some of the best code I've seen
|
| There, fixed it for you.
|
| But I have to be fair, whenever I see a demand for
| "idio(ma)tic code" I know that this is a place to avoid,
| no matter if they are imperatively or functionally
| inclined.
| anon291 wrote:
| I mean sure. Realistically, I'm certain I would do that
| if I were working on a typical code base, which is why
| I'm in an extremely niche field where that sort of thing
| is valued. From my extremely biased perspective, these
| are the 'hard' problems that need solving, versus the
| general run of the mill operational things. That probably
| sounds pretentious, but it takes skills for both.
| freedomben wrote:
| Your story matches my experience, but it always makes me
| think, why did this person want to work with you in the
| first place?
|
| A great Haskell programmer (generally speaking) is going
| to be a culture misfit in any Java, C#, golang, etc shop.
| I know because I've been that miserable bastard who loves
| functional programming working with Java devs who don't
| know anything about FP and couldn't care less. To be
| clear I'm not saying you _can 't_ find a compatible Java
| shop (I actually did find a startup with a lot of Java
| devs who appreciated FP and used much of it in Java, and
| that was pretty great honestly), just that the odds are
| highly against you.
|
| My biggest advice to people who like FP is: Find a job in
| a language like Clojure, Elixir, Scala, etc. There are a
| lot more jobs than you'd think. But if you can't, Ruby
| and Javascript/Typescript can be pretty close depending
| on where you go. Talk to existing devs and see how feel
| about FP in general before you join though!
| Joker_vD wrote:
| Because for some reason there are no pure strict-by-default
| languages around.
| foldr wrote:
| Elm is one example of such. However, it's also an
| illustration of why these languages are rare. With a strict
| semantics there's an almost unbearable temptation to add
| library functions with side effects. Elm only avoided this
| fate by giving its BDFL strict control over which packages
| could access the JS FFI. But that upset a lot of people.
| tome wrote:
| PureScript is an example.
| simiones wrote:
| I think Idris is the best example of a pure strict-by-
| default language (that also supports totality checking, I
| believe).
| ReleaseCandidat wrote:
| > that also supports totality checking, I believe
|
| Yes, it does. It's also dependently typed.
| ReleaseCandidat wrote:
| All of them (especially newer ones) are, except Haskell
| (und some other, nowadays either obsolete or really obscure
| languages).
|
| Idris (2), PureScript, Elm, Unison, Roc, Lean (4), Koka,
| Flix (and some other I've forgotten about).
| beders wrote:
| I feel the same pay-off - but arrived at that point via
| Clojure. Immutable-first, aim for purity, ability to drop out
| of it when necessary.
|
| As stringent as you need it to be (static vs. dynamic types vs.
| specs), as flexible as you want it to be.
| IshKebab wrote:
| I feel like functional programming is pretty trivial. It's
| _pure_ programming that is very difficult.
|
| They're often conflated because Haskell is pure and functional
| and probably the most talked about heavily functional language.
|
| I certainly didn't know that impure functional languages like
| OCaml existed for ages.
| fire_lake wrote:
| Is Haskell pure?
|
| It has exceptions
|
| You can divide by zero
|
| It has unsafe IO primitives
| tome wrote:
| You're right: "pure" is not a well-defined concept. The
| well-defined concept that describes Haskell's benefits in
| this regard is "referential transparency". That means that
| this code let x = <definition of x>
| in ... x ... x ...
|
| (i.e. defining a variable x and then using it some number
| of times) is equivalent to ...
| <definition of x> ... <definition of x> ...
|
| Seen in the opposite direction (transforming the bottom
| code to the top code) this means that extracting repeated
| code is always a valid thing to do. It's not valid in most
| other languages, and certainly no mainstream ones.
| simiones wrote:
| Well, technically that isn't true if you use, for
| example, unsafePerfomIO in the defintion of x.
| Referential transparency is still a spectrum, just like
| purity. Haskell is much closer to purity than the vast
| majority of languages out there.
|
| Also, even if Haskell were perfectly pure, the fact that
| it uses lazy evaluation is far more important to actually
| being able to make use of referential transparency. In a
| strict language you will still see a massive performance
| differences if replacing the first version with the
| second, in the most common cases.
| tome wrote:
| > technically that isn't true if you use, for example,
| unsafePerfomIO in the defintion of x
|
| Ah, well, regardless of whether it holds in Haskell,
| referential transparency is a well-defined concept.
| Purity is _not_ a well-defined concept (at least as far
| as I know. Please share a formal definition if you have
| one!). That 's _primarily_ what I 'm trying to say.
|
| But I also disagree with your point about
| unsafePerformIO. In practice, nothing in Haskell violates
| referential transparency in a significant way. Who knows
| why? It's an emergent phenomenon that in principle need
| not have occurred, but in practice it did. Debug.Trace
| and similar are about the only things that technically
| violate referential transparency (and they are
| _extremely_ tame).
|
| > the fact that it uses lazy evaluation is far more
| important to actually being able to make use of
| referential transparency
|
| Yes, I agree with that.
| simiones wrote:
| It seems that someone did come up with a formal
| definition to try to capture the concept [0], though I
| haven't looked into the details to see whether it really
| matches the colloquial use of the terms "pure" and
| "impure" functional programming. In short, the formal
| definition they came up with is that a language is pure
| if the result of a valid program is the same under any
| parameter passing strategy.
|
| I should note that I agree that, in practice, Haskell is
| almost always pure and/or referentially transparent. I
| was just pointing out that _technically_ the GP was
| correct that it 's not perfectly 100% so.
|
| [0] https://www.cambridge.org/core/journals/journal-of-
| functiona...
| tome wrote:
| Sabry's definition of "pure" fails to satisfy me for two
| reasons:
|
| 1. It assumes that the language is "a conservative
| extension of the simply typed l-calculus". That's rather
| high-powered yet also really restrictive! Haskell doesn't
| satisfy that requirement. It also assumes the language
| has functions. Expression languages (i.e. ones without
| functions) are perfectly reasonable languages and it
| makes sense to ask whether they are pure.
|
| 2. It assumes that a language putatively has multiple
| evaluation orders (which I suppose is a consequence of
| the assumption "It is a conservative extension of the
| simply typed l-calculus"). Haskell doesn't have multiple
| evaluation orders. It has one! (notwithstanding you can
| influence it with seq/!)
|
| If you unpick the essence of what Sabry's really saying
| you find you can translate it into the Haskell world
| through imposing two conditions:
|
| C1. soundness of the b-axiom (a.k.a. referential
| transparency) (this plays the role of Sabry's condition
| that call by need and call by value have the same
| result).
|
| C2. That let x = <definition of x> in
| ...
|
| gives the same result as let !x =
| <definition of x> in ...
|
| whenever the latter terminates. (This plays the role of
| Sabry's condition that call by name and call by value
| have the same result.) I omitted this condition from my
| original. I probably shouldn't have because technically
| it's required, but it risks getting into the weeds of
| strictness versus laziness.
|
| So Sabry's definition of "pure" is a long-winded and
| restricted way saying something that can be much more
| conveniently expressed by C1 and C2. If you disagree with
| me, please demonstrate a property of purity that can't be
| deduced from C1 and C2!
|
| > I should note that I agree that, in practice, Haskell
| is almost always pure and/or referentially transparent. I
| was just pointing out that technically the GP was correct
| that it's not perfectly 100% so.
|
| OK, fine, but I also said the GP was correct! I am keen
| to point out, however, that exceptions (including
| division by zero) do not violate referential transparency
| (and if someone thinks they violate "purity" that may be
| a sign that "purity" is ill-defined).
| afiori wrote:
| I feel like exceptions where added as a mix of "look we can
| do that too" and "maybe if so many functions return
| optional values then it is going to be too much of a pain
| to use"
|
| In hindsight I think few would now regret not having added
| them in the first place.
| fire_lake wrote:
| > maybe if so many functions return optional values then
| it is going to be too much of a pain to use
|
| I strongly believe that there is a point in the PL design
| space that makes optionals everywhere usable. Maybe
| Haskell can still be the language that delivers this.
| tome wrote:
| To plug my own solution, my effect system Bluefin makes
| exceptions visible in the type, well-scoped, and also
| freely composable with all other effects:
|
| https://hackage.haskell.org/package/bluefin-0.0.3.0/docs/
| Blu...
| afiori wrote:
| >It has unsafe IO primitives
|
| To be tongue in cheek then it also has the side effect of
| heating the CPU.
| IshKebab wrote:
| It is pure in the same way that Rust is memory safe. That
| is too say there are a tiny number of exceptions/escape
| hatches, but they are not meant to be the norm. Every day
| programming doesn't involve them.
|
| Exceptions aren't impure anyway.
| baby wrote:
| Sir have you heard of GADTs
| realPtolemy wrote:
| Or Elixir! Quite easy to grasp as well.
| __rito__ wrote:
| For the benefit(s) that you list, which are the best learning
| resources for F#?
| neonsunset wrote:
| https://fsharpforfunandprofit.com/
| dejvid123 wrote:
| I would suggest Scala as FP for beginners. It doesnt forces you
| to do pure functions. And its really beginners friendly to
| start with.
| empath75 wrote:
| I was a college dropout and self taught bash and python
| programmer and quite some time ago, I read about Haskell,
| decided to teach myself to use it, and then realized I had
| absolutely no idea what programming actually was, and basically
| spent the next 15 years teaching myself computer science,
| category theory, abstract algebra and so on, so that I could
| finally understand Haskell code.
|
| I still don't understand Haskell, but it did help me learn Rust
| when I decided to learn that. And I think I could explain what
| a monad is.
|
| edit: It's a data structure that implements flat map. Hope this
| saves someone else a few years of their life.
| bspammer wrote:
| I think that's a good starting definition for programmers,
| but still could cause confusion when you run into something
| like IO in Haskell. IO isn't really a data structure, and
| it's hard to fit the "flat map" concept to it.
| crabbone wrote:
| > I still don't understand Haskell
|
| It's not you. Haskell has very bad syntax. It's not hard to
| understand it, it you rewrite the same things in something
| saner. Haskell was developed by people who enjoy one-liners
| and don't really need to write practical programs.
|
| Another aspect of Haskell is that it was written by people
| who were so misguided as to think that mathematical formulas
| are somehow superior to typical imperative languages, with
| meaningful variable names, predictable interpretations of
| sequences of instructions etc. They, instead, made it all
| bespoke. Every operation has its own syntax, variables are
| typically named as they would in math formulas (eg. X and
| X'). This makes no sense, and is, in fact, very harmful when
| writing real-world programs, but because, by and large,
| Haskell never raises to the task of writing real-world
| programs, it doesn't deter the adepts from using it.
| tome wrote:
| That's what (some) other people do. None of that stops you
| writing Haskell in whatever style you want, with meaningful
| variable names, curly braces and semicolons!
| crabbone wrote:
| Unfortunately, _writing_ isn 't even half the battle.
| Before you start writing, you need to read a lot. And
| Haskell code is, in general, atrocious. It always feels
| like there was a normal way to do something, but the
| author decided to chose the most convoluted way they can
| imagine to accomplish the same thing for the sake of a
| bizarre fashion sense.
| agentultra wrote:
| You knew Paul Hudak, Simon Peyton Jones, Phil Wadler, etc?
| Were they thinking about the benefits of mathematical
| formulas over program counters and procedural keywords when
| designing Haskell?
|
| I was under the impression from the _History of Haskell_
| [0] that they were interested in unifying research into
| lazy evaluation of functional programming languages.
|
| > This makes no sense, and is, in fact, very harmful when
| writing real-world programs,
|
| Gosh, what am I doing with my life? I must have made up all
| those programs I wrote on my stream, the ones I use to
| maintain my website, and all the boring line-of-business
| code I write at work. /s
|
| In all seriousness, Haskell has its warts, but being
| impractical isn't one of them. To some purists the
| committee has been overly pragmatic with the design of the
| language. As far as functional programming languages go
| it's pretty hairy. You have "pure" functions in the base
| libraries that can throw runtime exceptions when given the
| wrong values for their arguments (ie: the infamous _head_
| function). _Bottom_ , a special kind of null value, is a
| member of every type. There exist functions to escape the
| type system entirely that are used with some frequency to
| make things work. The committee has gone back more than
| once to reshape the type-class hierarchy much to the
| chagrin of the community of maintainers who had to manually
| patch old code or risk having it not longer compile on new
| versions of the base libraries. These are all hairy,
| pragmatic trade-offs the language and ecosystem designers
| and maintainers have had to make... because people write
| software using this language to solve problems they have
| and they have to maintain these systems.
|
| [0] https://www.microsoft.com/en-us/research/wp-
| content/uploads/...
| jiiam wrote:
| Just to give a different pov I find Haskell very intuitive,
| and particularly I find that code written by other people
| is very easy to understand (compared to Java or TypeScript
| at least).
|
| And by the way x and x' are totally fine names for a value
| of a very generic type (or even a very specific type
| depending on the circumstances), as long as the types and
| the functions are decently named. I mean, how else would
| you call the arguments of
|
| splitAt :: Eq a => a -> [a] -> [[a]]
|
| ?
|
| There is no need for anything more complex than
|
| splitAt x xs = ...
| crabbone wrote:
| I would recommend neither of those.
|
| Haskell has very bad syntax (with extensive backing from
| Microsoft, iirc the guy who writes the compiler is a
| Microsoft's Research employee).
|
| F# is a straight-up Microsoft's language.
|
| It doesn't matter what other benefits it has. Just don't touch
| anything created by that company, and you will have one fewer
| regrets in your life.
|
| But, if you still want a language from that category: SML or
| Erlang would be my pick.
| fransje26 wrote:
| > Just don't touch anything created by that company, and you
| will have one fewer regrets in your life.
|
| :-)
| square_usual wrote:
| SPJ has left MSR and is now at Epic games, working on a new
| PL. However, even while he was at MSR, MS didn't really have
| a say in how Haskell was developed.
| crabbone wrote:
| Well, MS didn't have to do anything. It's enough that they
| have (or had) the opportunity to do something.
|
| There isn't an Overmind in MS that in a creepy voice tells
| you to spawn more overlords. Less than that, there doesn't
| need to be a written document that tells you to give money
| to MS or your data etc. There's just a general accepted
| understanding among the people who run that company that
| ends justify the means. And by "ends" they mean them and
| their investors getting rich.
|
| If Haskell compiler could've been turned into a money-
| making machine, and it only required killing off half of
| Haskell programmer, MS would be working overtime on the
| plan to hide the bodies, but they'd never even consider the
| possibility of killing being bad... (metaphorically
| speaking, hopefully)
| czhu12 wrote:
| > doesn't require every single function to be pure
|
| having never done F# or haskell, doesn't that start getting
| into the territory of languages that encourage functional
| programming like ruby or javascript (modern javascript)?
| Bostonian wrote:
| In modern Fortran, functions should be pure (although the
| language does not require this), and procedures that mutate
| arguments are made subroutines (which do not have return
| values).
| pklausler wrote:
| Note that Fortran's interpretation of the term "pure"
| bizarrely allows a "pure" subprogram to depend on mutable
| state elsewhere (in a host, a module, or a COMMON block). So
| Fortran's "pure" functions aren't referentially transparent.
|
| (F'2023 added a stronger form of "pure" and calls it
| "simple", but it didn't strengthen the places where a "pure"
| procedure should be required to be "simple", such as DO
| CONCURRENT, so being "simple" will be its own reward, if any
| compiler actually implements it. And a "simple" function's
| result value can still depend on a mutable pointer target.)
| sterlind wrote:
| I really wanted to like F#, and I kinda do, but it has a number
| of quirks, compiler issues and cracks in the design that are
| getting worse:
|
| First off, the compiler is _single-pass._ All your definitions
| have to be in order. This even extends to type hints - it can
| 't use clues to the right to deduce the type of an expression
| on the left. This is supposedly for perf reasons, but the
| compiler can become _extremely slow_ because the inference
| engine has to work so hard - slower than GHC for sure.
|
| Speaking of slowness, Haskell is surprisingly fast. Idiomatic
| Haskell can be within 50% the perf of C, since its laziness and
| purity unlock powerful optimizations. F# is eager and the
| compiler doesn't do anything fancy. Perf often makes you reach
| for mutable state and imperative structure, which is
| disappointing.
|
| The OOP paradigm feels bolted on. Should you use classes or
| modules? Pure functions or members? It depends, what mood are
| you in? Unfortunately only member functions support overloads,
| and overloads are useful for some SFINAE-type patterns with
| `inline` functions, so they get a bit overused.
|
| `ref` struct support, which is vital for zero-copy and
| efficient immutable data, have very primitive support. even C#
| is ahead on this.
|
| Very limited support for implicit conversions, no support for
| type classes and no function overloading leaves F# with nothing
| like a numeric tower you'd have in Lisp, and makes building
| something like Numpy clunky.
|
| I use C# at work, and I love Haskell, so I really wanted to
| love F#. But it just doesn't get the love it needs from MS, and
| some design decisions aren't aging well - particularly as C#
| itself evolves in directions that are tricky for F#'s aging
| compiler to support.
| stoorafa wrote:
| Had a lot of fun reading this. I'd love to see some of the
| author's code to get a sense what the journey produced, if that's
| even possible
| iso8859-1 wrote:
| https://github.com/esilverm
| TrackerFF wrote:
| What's the benefit of learning a PURE functional programming
| language, opposed to just using a language which has adapted the
| best bits and pieces from the functional programming paradigm?
|
| Given that you want write code that sees "real world" use, and is
| used to handle data and events from the real world. To me,
| sometimes the line between optimized code and intellectual
| curiosity blurs.
| mrkeen wrote:
| This is all myth. People don't write Haskell, because they read
| why other non-Haskellers also don't write Haskell, based on
| what other non-Haskellers wrote.
|
| > a language which has adapted the best bits and pieces from
| the functional programming paradigm?
|
| Why write in a statically-typed language when dynamically-typed
| languages have adapted the best bits and pieces from
| statically-typed languages?
| cosmic_quanta wrote:
| > Why write in a statically-typed language when dynamically-
| typed languages have adapted the best bits and pieces from
| statically-typed languages?
|
| Unfortunately, dynamically-typed languages haven't adapted
| the best bit from statically-typed languages: that all types
| are enforced at compile-time!
| mrkeen wrote:
| Yep, that's the parallel I was going for.
|
| Functional languages give you the same output for the same
| input, and almost-functional languages ... probably give
| you the same output for the same input?
| Skinney wrote:
| > What's the benefit of learning a PURE functional programming
| language
|
| 1. It makes it easy to learn how to structure a program in a
| pure way, which is hard to do in languages that offers you a
| easy way out.
|
| 2. Since "everything" is pure, writing tests is easier.
|
| 3. You know for certain that if you discard the result of a
| function call, all the side-effects that it would normally
| trigger would be stopped as well.
|
| 4. A program where all side-effects are guaranteed to be pushed
| to the boundaries, is a program that's easy to reason about.
|
| > a language which has adapted the best bits and pieces [...]
|
| Languages that has adapted to best bits and pieces from X, Y, Z
| tend to be worse than a language specifically for X, Y and Z.
|
| For instance, Java supports functional programming but
| functional programming languages are much better at it because
| they were designed for that specific paradigm. In the same
| vein, sure you can write pure programs in F#, but not as easily
| as in Haskell that was designed for doing just that.
|
| > and is used to handle data and events from the real world
|
| Pure code really only means (in practice) that side-effects are
| controlled, which is generally very helpful. It forces you to
| structure programs in a way which makes it easy to pinpoint
| where data is coming in, and where data is going out. It also
| makes for easier testing.
|
| Being able to know, definetly, the answer to "will calling foo
| perform a network request" without having to read the source
| for foo is quite nice, especially when dealing with third-party
| code.
|
| All this said, I probably wouldn't begin with Haskell. A
| language like Elm is much better suited for learning writing
| pure programs.
| neonsunset wrote:
| The problem with Haskell is that it's slow and memory-heavy
| (and OCaml is the same, but worse). F# and Scala (and
| Clojure?) are pretty much the only reasonably usable FP
| languages.
| crabbone wrote:
| Where are you getting your info from?
|
| Typical OCaml programs, when compared to similar C++ would
| be slower but use less memory.
|
| F# and Scala are both OCaml in disuse. I don't know what
| you mean by "reasonable"... but, if the idea is "easy to
| reason about", then these two don't particularly stand out
| much.
|
| Languages that are easy to reason about would be generally
| in the category where you need to do fewer translations
| before you get to the way the program is executed (i.e.
| bytecode adds an extra step, thus making a language harder
| to reason about). Also, languages with fewer primitives are
| easier to reason about, because the program text becomes
| more predictable.
|
| In general, "functional" languages are harder to reason
| about when compared to imperative, because computers
| inherently don't work in the way the programs are modeled
| in "functional" languages, so there will be some necessary
| translation layer that transforms an FP program into a real
| computer program. There are people who believe that FP
| programs are easier to reason about due to the lack of side
| effects. In my experience, the lack of side effects doesn't
| come close to compensating the advantages of being able to
| map the program to what computer actually does.
|
| All kinds of behind-the-scenes mechanisms in the language,
| s.a. garbage collector, make the reasoning harder too, in a
| sense. We pretend that GC makes reasoning easier by making
| a mental shortcut: we pretend that it doesn't matter when
| memory is freed. But, if you really want a full picture, GC
| adds a whole new layer of complexity when it comes to
| understanding a program.
|
| Yet another aspect of reasoning is the ability of reasoner
| to act on their reasoning. I.e. the reasoning might be
| imperfect, but still allow to act (which is kind of the
| human condition, the way we are prepared to deal with the
| world). So, often, while imperative programs cannot be
| _formally_ easily reasoned about, it 's easy to
| _informally_ reason about them to be efficient enough to
| act on that reasoning. "Functional" programs are usually
| the reverse: they are easier to reason about formally, but
| they are very unnatural to the way humans reason about
| everyday stuff, so, acting on them is harder for humans.
|
| "Functional" languages tend to be more in the bytecode + GC
| + multiple translations camp. And, if forced to choose with
| these constrains, I'd say Erlang would be the easiest and
| the best designed language of all the "popular" ones. SML
| would be my pick if you need to get into the world of
| Haskell, but deciphering Haskell syntax brings you to the
| boil.
| neonsunset wrote:
| Heh, no.
|
| You are suggesting to replace FP languages with powerful
| type systems that perform marginally slower than C# and
| Java (and can access their ecosystems) with a language
| that is dynamically typed and performs, in most
| situations, marginally slower than PHP and marginally
| faster than Ruby.
| freedomben wrote:
| Agree with all the reasons, but number 1 is really the most
| important:
|
| > 1. It makes it easy to learn how to structure a program in
| a pure way, which is hard to do in languages that offers you
| a easy way out.
|
| When there's an escape hatch, you _will_ reach for it at some
| point. It helps with getting things done, but you never end
| up really confronting the hard things when you have that, and
| the hard things are an important part of the learning
| /benefit.
| elbear wrote:
| What's the benefit?
|
| You start to see functions as self-contained things, as lego
| blocks. All the logic of the function is there in the function.
| It only works on values it receives as inputs (it can't read
| global variables). It only outputs its results (it doesn't
| assign them to some other global variable that you have to
| track down).
|
| This makes your code modular. You can add a function in a chain
| of functions, if you want to perform an extra transformation.
| Or, you can replace a function with a different one, if you
| want to change something about the logic.
| Dylan16807 wrote:
| Is there a benefit if you're already familiar with writing
| functions like that? Is it wrong for me to expect that most
| programmers are already familiar with functions that only use
| their inputs, but treat that style as significantly more
| optional?
|
| I wrote pure functions for a minute there but that's not the
| same, a function that only uses its inputs can modify an
| object while a pure function would have to return a new
| object. But, similarly, I bet that a lot more people know
| about pure functions than have any working knowledge of
| Haskell.
| elbear wrote:
| It seems you only focused on one of the conditions I
| mentioned.
|
| You have to follow both rules: the one about inputs and the
| one about outputs.
|
| This is like a contract. If you enforce it throughout your
| program, you gain some guarantees about your program as a
| whole.
| Dylan16807 wrote:
| I was looking at both rules, and specifically I was using
| the long version where you said "it doesn't assign them
| to some other global variable that you have to track
| down". If you pass in a mutable object then that's not
| "some other global variable".
|
| If I interpret "It only outputs its results" in a very
| strict way, that still allows having output and in/out
| parameters. The latter of which can break purity.
|
| Though you can break purity with just inputs:
| define f(o): return o.x let a = {x=1} f(a)
| a.x = 2 f(a)
|
| If you meant to describe pure functions then that's fine,
| that's why I addressed pure functions too, but I don't
| think your original description was a description of pure
| functions.
| elbear wrote:
| So, another definition of a pure function is that, for a
| particular input it will always return the same output.
|
| Your example respects the rule:
| f({x=1}) == 1 f({x=2}) == 2
|
| But it's true that the two rules I gave are not enough to
| make a function pure. Because I didn't say anything about
| I/O. So, a function that follows the rules about inputs
| and outputs, could still do I/O and change its outputs
| based on that.
|
| Starting from the question that gave birth to this whole
| thread: "What's the benefit of learning a PURE functional
| programming language..."
|
| The other benefit is that such a language forces you to
| be explicit about I/O. It does it in such a way that even
| functions that do I/O are pure. The good part is that, if
| you use it long enough, it can teach you the discipline
| to be explicit about I/O and you can use this discipline
| in other languages.
|
| For example, this is how I see this principles being used
| in Python:
|
| https://elbear.com/functional-programming-principles-you-
| can...
| Dylan16807 wrote:
| > Your example respects the rule:
|
| Every definition of purity I can find that talks about
| objects/references says that if you pass in the same
| object/reference with different contents then that's not
| pure.
|
| Your version differs from mine on that aspect. It passes
| two unrelated objects.
|
| > Starting from the question that gave birth to this
| whole thread: "What's the benefit of learning a PURE
| functional programming language..."
|
| I interpret saying a language is "purely functional" as
| being more about whether you're allowed to write anything
| that isn't functional. I can talk about BASIC being a
| "purely iterative" language or about "pure assembly"
| programs, without any implication of chunks of code being
| pure.
| simiones wrote:
| Purely functional language is pretty universally taken to
| mean that the language enforces function purity for all
| functions [perhaps with some minor escape hatches like
| Haskell's unsafePerformIO].
| mrkeen wrote:
| > Is it wrong for me to expect that most programmers are
| already familiar with functions that only use their inputs
|
| They'll experience no friction when using Haskell then.
| Haskell only refuses to compile when you declare "Oh yeah I
| know functions from other languages this is easy" but then
| do some mutation in your implementation.
| Dylan16807 wrote:
| > They'll experience no friction when using Haskell then.
|
| The question was what benefit you'd get from learning a
| functional language, though. Existing knowledge making it
| easier to switch to a functional language is the inverse
| of that.
|
| And there's no assumption they'll actually be making
| things in Haskell, so easy switching isn't by itself a
| benefit.
| mrkeen wrote:
| Yeah I can't really follow these threads.
|
| I saw:
|
| > What's the benefit of learning a PURE functional
| programming language, opposed to just using a language
| which has adapted the best bits and pieces from the
| functional programming paradigm?
|
| I also saw: Though you can break purity
| with just inputs: define f(o): return o.x
| let a = {x=1} f(a) a.x = 2 f(a)
|
| I don't know if that's the tail-end of a reductio ad
| absurdum which is trying to demonstrate the opposite of
| what it stated. Either way, to be clear, the above would
| be rejected by Haskell (if declared as a pure function.)
|
| I guess if you learn a functional language "which has
| adapted the best bits and pieces from the functional
| programming paradigm" then you might think that the above
| is broken purity, but if you learn a "PURE functional
| programming language" then you wouldn't.
| Dylan16807 wrote:
| The topic is what you would learn _from_ a pure
| functional language.
|
| A) You can learn and enforce full purity in other
| languages. B) You could also learn and adapt just the
| idea of clean inputs and outputs to those other
| languages.
|
| Both of those are valid answers! It's very hard to be
| completely pure if you're not currently using Haskell.
|
| The way they worded things, I wasn't sure which one they
| meant. They were describing option B, but I didn't know
| if that was on purpose or not.
|
| So I responded talking about both. Complete purity and
| just the idea of clean inputs and outputs.
|
| That code snippet is not some kind of absurd argument or
| strawman, it's there to demonstrate how the description
| they gave was not a description of purity. It's not aimed
| at the original question.
| kreyenborgi wrote:
| Don't think of it as being all pure code, think of it as
| tracking in the type system which parts of your code may launch
| the missiles and which parts can't. Given the following
| program, main :: IO () main = do
| coordinates <- getCoords launch trajectory
| where trajectory = calcTrajectory coordinates
| getCoords :: IO Coordinates getCoords = -- TODO
| launch :: Trajectory -> IO () launch = -- TODO
| calcTrajectory :: Coordinates -> Trajectory
| calcTrajectory = -- TODO
|
| I can look at the types and be reasonably certain that
| calcTrajectory does no reads/writes to disk or the network or
| anything of that sort (the part after the last arrow isn't `IO
| something`), the only side effect is perhaps to heat up the CPU
| a bit.
|
| This also nudges you in the direction of an Functional Core,
| Imperative Shell architecture
| https://www.destroyallsoftware.com/screencasts/catalog/funct...
| foobazgt wrote:
| FYI, I think you meant functional core, imperative shell.
| kreyenborgi wrote:
| haha yes, thanks!
| Barrin92 wrote:
| >as tracking in the type system which parts of your code may
| launch the missiles
|
| given that Haskell is lazy by default there's a million ways
| to shoot yourself in the foot through memory leaks and
| performance issues (which is not unlike the problems the IO
| type attempts to make explicit in that domain), so I never
| really understand this kind of thing. Purity doesn't say much
| about safety or semantics of your code. By that logic you
| might as well introduce a recursion type and now you're
| tagging everything that is recursive because you can easily
| kill your program with an unexpected input in a recursive
| function. To me this is just semantics you have to think
| through anyway, putting this into the type system just ends
| up creating more convoluted programs.
| zogrodea wrote:
| I can't speak for others, but I never really understood the
| benefits of functional programming when my language pretty much
| allowed unbounded mutation anywhere. I would say there's a
| chance for impure languages to impede you in learning what
| functional programming is about (or at least my experience with
| F# and OCaml did not really help as much as it otherwise could
| have I think).
|
| Your mileage might vary, but I've heard advice from others to
| learn Haskell and "go off the deep-end" because of people
| citing similar reasons.
| agumonkey wrote:
| In a way, one benefit is the whole ecosystem / culture / idioms
| built on top. Haskellers went further in that direction than
| most languages (except maybe scalaz and some hardcore
| typescript devs).
| keybored wrote:
| > Given that you want write code that sees "real world" use,
| and is used to handle data and events from the real world.
|
| Real world? As opposed to what?
|
| Is there any benefit to answering such polemical questions as
| if they are not rhetorical?
| lukan wrote:
| As opposed to the abstract academic world?
|
| The only time I had contact with Haskell was in university
| and I did not see it appealing back then, nor now, nor have I
| ever seen a program that I use, written in it.
|
| So learning a bit of pure Haskell might have been beneficial
| for me to become a better programmer, but I still fail to see
| it being more than that - a academic language. Useful for
| didactic purposes. Less to actually ship software.
| padthai wrote:
| > nor have I ever seen a program that I use, written in it
|
| The only mass market Haskell software that I know of is
| Pandoc. Others like Shellcheck and Postgrest are popular in
| their niche.
|
| I am not sure that Haskell is faring worse that other
| programming languages in its level of popularity, like
| Julia, Clojure or Erlang.
| lukan wrote:
| Pandoc seems useful, but maybe "mass market" is a bit of
| an overstatement?
|
| And since many programmers like myself had to learn
| Haskell, I think Haskell should have a better head start
| and be in a better position, if it would be so useful for
| "real world" use cases.
|
| But please don't take this as an attack on haskell. I
| have nothing against the language, or its users and I did
| not suffered because of it in university, I am just
| curious on the appeal. Because I love clean solutions,
| but I also want to ship things. So part of me are
| wondering if I am missing out, but I so far I see not
| much convincing data. (But I am also mainly interested in
| high performance and real time graphics and haskell is
| really not the best here)
| itishappy wrote:
| Pandoc is _the_ standard for markdown conversion. Check
| out the comments in this recent thread (or pretty much
| any thread where markdown is mentioned):
|
| https://news.ycombinator.com/item?id=40695628
|
| https://hn.algolia.com/?q=markdown
| lukan wrote:
| I don't think markdown conversion is a mass market
| application, but maybe personally I will indeed use it
| soon, so that would be something I guess ..
| padthai wrote:
| I am not a user of the language (although I learned it
| like you). I just came to chime in that (a) there is at
| least one very popular software written in Haskell and
| (b) Haskell seems to ship a good amount of software for
| its popularity.
|
| Haskell never got the "killer framework" like Rails or
| Spark that allowed to become more mainstream, even if it
| was teached in Universities all over the world.
| lukan wrote:
| "Haskell never got the "killer framework" like Rails or
| Spark that allowed to become more mainstream"
|
| But why is that the case?
|
| Thinking about writing a "killer framework" with huskell
| gives me a headache. Doing UI in huskell? Eventloop?
| Callbacks? Is that even possible, without doing awkward
| workarounds?
| __s wrote:
| Haskell interfaces with the real world. ST allows for
| mutability in pure context
|
| https://github.com/serprex/Fractaler/blob/master/Fractaler.h...
| fractal renderer I wrote in highschool, has mouse controls for
| zoom / selecting a variety of fractals
|
| https://github.com/serprex/bfhs/blob/master/bf.hs brainfuck
| interpreter which mostly executes in pure context, returning
| stdout with whether program is done or should be reinvoked with
| character input. Brainfuck tape implemented as zipper
|
| your program may exist in real world, but most of it doesn't
| care about much of the real world
| cosmic_quanta wrote:
| Pure functional programming doesn't preclude side-effects like
| IO; it makes side-effects explicit rather than implicit!
|
| At my previous job, we used pure functional programming to
| ensure that custom programs only had access to certain side-
| effects (most importantly, not IO). This meant that it was
| trivial to run these custom programs in multiple environments,
| including various testing environments, and production.
| agentultra wrote:
| There's a video game on steam you can buy with real dollars
| built in Haskell.
|
| I work full-time writing Haskell. Fintech stuff. No shiny
| research going on here.
|
| I've written some libraries and programs on my stream in
| Haskell. One is a client library for Postgres' streaming
| logical replication protocol. I've written a couple of games.
| Working on learning how to do wave function collapse.
|
| Believe it or not, functional programmers -- even ones writing
| Haskell -- often think about and deliver software for "real
| world," use.
| tasuki wrote:
| > There's a video game on steam you can buy with real dollars
| built in Haskell.
|
| Link? Story?
| agentultra wrote:
| https://www.reddit.com/r/haskell/comments/z98ubk/comment/iy
| f...
| js8 wrote:
| In my experience, I only really learned how to write small
| functions after Haskell. The discipline it forces on you is a
| good training.
| leononame wrote:
| Great read! Can anyone here recommend a good resource for
| learning Haskell that's in the style of "Text-Mode Games as First
| Haskell Projects"? Haskell has been on my radar since forever,
| and I've got some FP concepts internalized by making a side
| project in F#, but I have no idea what a monad really is and a
| fun prohect to code along might be perfect.
| brabel wrote:
| I've been learning Unison [1] and I highly recommend. It's a
| Haskell-like language, but with some really interesting ideas
| around how code should be managed and distributed. They also
| use use algebraic effects (represented with "abilities" in
| Unison) instead of Monads, which gives some interesting
| advantages [2].
|
| [1] https://www.unison-lang.org/
|
| [2] https://www.unison-
| lang.org/docs/fundamentals/abilities/for-...
| tasuki wrote:
| Have you written anything in Unison yet? To me Unison feels
| extremely ahead of its time. They clearly thought things
| through and aren't afraid to challenge the status quo. Maybe
| a bit too much ahead of its time even...
|
| I fear `ucm` a little. You mean I can't version my things
| with git? How do I... ehh, do anything? And how is the
| deployment story if one chooses not to use the Unison cloud?
| carbonatom wrote:
| Can you or someone else familiar with Unison tell us what
| specific things about Unison feel ahead of its time? I
| don't know Unison so these things will be a good motivation
| for me to learn Unison.
| cflewis wrote:
| Unison tries to swallow the whole elephant all at once,
| which is probably what the author is getting at.
|
| * `ucm` is like a coding assistant that sits with you the
| whole time. You don't grep through code to find snippets
| or anything, you use `ucm`. It does a whole lot more, but
| that's just the trivial example.
|
| * You need `ucm` because Unison stores code as a syntax
| tree, not text. This is awesome because
| versioning/dependency conflicts/rename issues just go
| away. This is not awesome because nothing else knows how
| to understand this: other source control systems will
| just not work.
|
| * It's really trying to drag functional coding into this
| decade and what we use code for in production. It's not
| trying to be Haskell which is a great language, but
| doesn't (to me) feel like it was designed to do something
| like a simple web app.
|
| * It is supposed to do distributed cloud computing
| without modification (this smells like where the VC money
| came from), but again, you have to use their platform
| because other clouds don't understand Unison.
|
| The list goes on.
|
| Each individual piece of Unison I think is really great.
| I love the `ucm` model of having an assistant sit next to
| you the whole time. What I don't love is that there is
| just _so much_ learning placed on the developer. To
| understand Unison, you need to understand a lot of what
| they're doing, a lot of what they are doing is novel, and
| so you have to eat the whole elephant that they are. I
| don't know if there was a path where they could have
| eaten the elephant one bite at a time, but it really
| makes the onboarding onerous.
| lordwarnut wrote:
| Kind of ironically I've enjoyed the 'Write Yourself a Scheme in
| 48 Hours'[1] which goes over how to write your own Scheme in
| Haskell. It introduces some of the more interesting monads
| although I'm not sure how idiomatic it is.
|
| [1]
| https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_...
| kreyenborgi wrote:
| https://learn-haskell.blog/
|
| > In this book, we will implement a simple static blog
| generator in Haskell, converting documents written in our own
| custom markup language to HTML.
|
| > We will: Implement a tiny HTML printer
| library Define and parse our own custom markup language
| Read files and glue things together Add command line
| arguments parsing Write tests and documentation
|
| > In each chapter of the book, we will focus on a particular
| task we wish to achieve, and throughout the chapter, learn just
| enough Haskell to complete the task.
| fire_lake wrote:
| Write a few computation Expression builders in F# and monads
| will quickly make sense.
| ratg13 wrote:
| https://www.learnyouahaskell.com/
| williamcotton wrote:
| This may help? (Just an example of a monad pattern in use to
| build a burrito!)
|
| https://www.williamcotton.com/articles/a-burrito-is-a-monad
| tombert wrote:
| Haven't you heard? Monads are burritos!
|
| In all seriousness, it's not "text mode", but one of the things
| that I felt really showed how cool Haskell and a friend could
| pure model could be a was Netwire and Functional Reactive
| Programming. It allowed me to design graphical applications the
| way I always wanted to instead of how they're typically
| structured in imperative languages. There are lots of tutorials
| out there for making little games with it.
| tombert wrote:
| Ugh, autocorrect and it's too late to edit.
|
| I was suggesting the Netwire library, an FRP library for
| Haskell.
| trealira wrote:
| "Programming in Haskell" by Graham Hutton has a few small text
| mode games in the second half of the book: Nim, Hangman, the
| game of life, and Tic-Tac-Toe; and it walks you through the
| minimax algorithm. The author also implements a solution to the
| countdown problem, which is hard to explain, so as an example,
| you're given a sequence of numbers [1, 3, 7, 10, 25, 50] and
| the target 765; a correct solution is (1+50)*(25-10).
|
| The first half of the book is more geared towards a newbie to
| functional programming in general.
| neonate wrote:
| https://web.archive.org/web/20240617035104/https://duckrabbi...
| drwu wrote:
| When Haskell was a hot topic around two decades ago, ML was also
| quite often discussed. Today ML almost only means machine
| learning
| firesteelrain wrote:
| That's what I remember from my Computer Science classes
| 2001-2002. Standard ML was hard to learn back then for a newbie
| especially a Computer Science newbie.
| dboreham wrote:
| ML exists today as OCaml and F#
| iNic wrote:
| https://archive.ph/gqOsY
| usgroup wrote:
| In my opinion, if you are after the mystical experience of
| understanding functional programming, you're better off learning
| Prolog. I think it has more to offer in terms of insight, because
| wrapping your head around the language only takes a couple days,
| but wrapping your head around its consequences is a gift which
| keeps on giving for quite some time.
|
| Immutable functional programming is basically what 80% of your
| Prolog code will look like. The benefit is that you'll be able to
| understand how everything works from end-to-end.
| Valodim wrote:
| Prolog is a logic programming language though, I wouldn't
| expect it to have a lot of overlap with functional programming?
| usgroup wrote:
| Prolog variables are immutable by default. Data structures
| are the same as the immutable functional programming
| counterparts (no arrays, and tree based everything).
| Recursion is the only way to loop. Map, filter, fold(l/r),
| reduce, accumulate, etc, are staple predicates.
|
| As I said, 80% of your code, or more, will look just like a
| functional programme.
| cess11 wrote:
| An execution pipeline in a functional language is just half a
| relation in Prolog. Prolog allows you to also run it
| 'backwards', you can provide the output and have it figure
| out what the inputs would need to be.
| usgroup wrote:
| Run in backwards, so they say, but not really. Most things
| you'll write can't run backwards. You have to write them in
| a special way for that to be possible, and even then what
| it means is that you can do a depth first search to find
| the value.
|
| There are some useful extensions like clpfd and asp but
| really if what you're doing is solving a constraint
| programming problem, you're much better of with OR-tools or
| MiniZinc.
|
| Prolog is beautiful. I have practically no use for it. I've
| struggled to find something I can do better with Prolog
| than other tools, but I just love it aesthetically and
| that's enough for me sometimes.
| marcosdumay wrote:
| It's also biased into informal definitions, mixing side
| effects with logic, and encapsulating complex behavior
| together. It's way more biased into being a scripting
| language, while Haskell has all those biases pointed at being
| an application language.
|
| So, I'd say that both languages lead people into very
| different programing styles.
| tomcam wrote:
| Sure, lord it over the rest of us peons. We can't all be
| overachievers, you know.
| revskill wrote:
| Yes, it's better to spend 15 years to learn Haskell than keep
| creating messy imperative programs without knowing how to
| improve.
| lonk wrote:
| Just "15 years" or Nothing
| js8 wrote:
| Are you saying they Maybe learned Haskell?
___________________________________________________________________
(page generated 2024-06-17 23:02 UTC)