• CanadaPlus@lemmy.sdf.org
    link
    fedilink
    arrow-up
    2
    ·
    4 days ago

    I’m not sure what you mean by “locality of reference”. I assume you mean something other than the traditional meaning regarding how processors access memory?

    Shit! Sorry, got my wires crossed, I actually meant locality of behavior. Basically, if you’re passing a monad around a bunch without sugar you can’t easily tell what’s in it after a while. Or at least I assume so, I’ve never written anything big in Haskell, just tons of little things.

    To give a concrete example:

    Yeah, that makes tons of sense. It sounds like Transaction is doing what a string might in another language, but just way more elegantly, which fits into the data generation kind of application. I have no idea how you’d code a game or embedded real-time system in a non-ugly way, though.

    It also has a type system that is far, far more powerful than what mainstream imperative programming languages are capable of.

    Absolutely. Usually the type system is just kind of what the person who wrote the language came up with. The Haskell system by contrast feels maximally precise and clear; it’s probably getting close to the best way to do it.

    • expr@programming.dev
      link
      fedilink
      arrow-up
      1
      ·
      edit-2
      3 days ago

      Shit! Sorry, got my wires crossed, I actually meant locality of behavior. Basically, if you’re passing a monad around a bunch without sugar you can’t easily tell what’s in it after a while. Or at least I assume so, I’ve never written anything big in Haskell, just tons of little things.

      I’m not sure if I entirely follow, but in general you actually have much better locality of behavior in Haskell (and FP languages in general) than imperative/OOP languages, because so much more is explicitly passed around and immutable. Monads aren’t an exception to this. Most monadic functions are returning values rather than mutating some distant state somewhere. Statefulness (or perhaps more precisely, mutable aliasing) is the antithesis of locality of behavior, and Haskell gives you many tools to avoid it (even though you can still do it if you truly need it).

      I’m not really sure what you mean by “don’t really know what’s in it after a while”. It might be helpful to remember that lists are monads. If I’m passing around a list, there’s not really any confusion as to what it is, no? The same concept applies to any monadic value you pass around.

      Yeah, that makes tons of sense. It sounds like Transaction is doing what a string might in another language, but just way more elegantly,

      I think you might have misunderstood what I was describing. The code we write doesn’t actually change, but the behavior of the code changes due to the particular monad’s semantics. So for example, let’s say I write a query that updates some rows in a table, returning a count of the rows affected. In this Transaction code block, let’s say I execute this query and then send the returned number of rows to an external service. In code, it looks like the API call immediately follows the database call. To give some Haskell pseudocode:

      example :: Transaction ()
      example = do
        affectedRows <-  doUpdateQuery
        doApiCall affectedRows
        return ()
      

      But because of how Transaction is defined, the actual order of operations when example is run becomes this:

      1. Send BEGIN;to DB
      2. Call doUpdateQuery
      3. Send COMMIT; to DB
      4. If transaction was successfully committed, execute doApiCall affectedRows. Otherwise, do nothing

      In essence, the idea is to allow you to write code where you can colocate your side-effectful code with your database code, without worrying about accidentally holding a transaction open unnecessarily (which can be costly) or firing off an API call mistakenly. In fact, you don’t actually have to worry about managing the transaction at all, it’s all done for you.

      which fits into the data generation kind of application. I have no idea how you’d code a game or embedded real-time system in a non-ugly way, though.

      I mean, you’re not going to be using an SQL database most likely for either of those applications (I realize I assumed that was obvious when talking about transactions, but perhaps that was a mistake to assume), so it’s not really applicable.

      I also generally get the impression that you have a notion that Haskell has some special, amorphous data-processing niche and doesn’t really get used in the way other languages do, and if that’s the case, I’d certainly like to dispel that notion. As I mentioned above, we have a pretty sizeable backend codebase written in Haskell, serving up HTTP JSON APIs for a SaaS product in production. Our APIs drive all (well, most) user interaction with the app. It’s a very good choice for the typical database-driven web and mobile applications of businesses.

      Ironically, I actually probably wouldn’t use Haskell for heavy data processing tasks, namely because Python has such an immense ecosystem for it (whether or not it should is another matter, but it is what it is)… What Haskell is great at is stuff like domain modeling, application code (particularly web applications where correctness matters a lot, like fintech, healthcare, cybersecurity, etc.), compilers/parsers/DSLs, CLI tools, and so on.*

      • CanadaPlus@lemmy.sdf.org
        link
        fedilink
        arrow-up
        1
        ·
        edit-2
        3 days ago

        Taking a wild guess at the source of the confusion, I should be clear that I love Haskell. It’s great for a lot of what I personally end up coding, namely math things that are non-heavy by computer standards but way too heavy to solve by hand. This isn’t naysaying.

        I mean, you’re not going to be using an SQL database most likely for either of those applications (I realize I assumed that was obvious when talking about transactions, but perhaps that was a mistake to assume), so it’s not really applicable.

        To be clear, I was introducing two new examples where I think this problem would come up. It could be that I’m missing something, but I’ve had this exchange a few times and been unimpressed by the solutions offered. The IO in those cases could get pretty spaghetti-ish. At that point, why not just use a state?

        Like, using a list, which is a monad, you could code a Turing machine, and it could have a tape specifying literally anything. I can’t imagine that one would ever come up, though.

        Ironically, I actually probably wouldn’t use Haskell for heavy data processing tasks, namely because Python has such an immense ecosystem for it (whether or not it should is another matter

        It certainly is, haha. If it’s heavy Python is just calling Fortran, C or Rust anyway.