Hacker News new | ask | show | jobs
by taylorphebus 3372 days ago
One big thing I was working on when I was studying FP was a processor simulator, which was basically 100% state with a bunch of things operating on different parts of the state at once. It wasn't clear to me that FP would get me any benefit even if my language would handle all the issues passing state around in that case.
2 comments

I think the more mutable state you have, and the more paths there are toward mutating that state, the more you can actually gain from using a language that forces you to be disciplined with how you handle it.
The hoops you have to jump through in a pure FP approach can make the problem 100x harder, though.

Some problems are just more inherently imperative and stateful. I use FP code when it makes sense, and I use imperative code when it makes sense.

There Is No Silver Bullet.

really? It's been a while since I've programmed in Haskell, but something like this is about right:

   data Data = Data { thing :: Int, that :: Boolean }}

   addToThing :: Int -> State Data Int
   addToThing n = do 
     x <- get thing
     modify (data -> data {thing: x + n})
     return x
It corresponds to this Typescript code:

   class Data {
     constructor(
        private thing: number, 
        private that: Number
     ) {}

     addToThing(n: number) {
        const old = this.thing
        this.thing = old + n
        return old
     }
   }
Except for the fact that you only need to see mutation if you want to, and you certainly can't use the mutation unless you want it.

To me it doesn't really look like jumping through hoops, but we're up front about the mutation so no-one's ever going to get a surprise.

There's no silver bullet, sure. But I don't think pure FP is really all that complex. It's mostly just different.

> But I don't think pure FP is really all that complex. It's mostly just different.

I can't help but think that people who say this, yourself included, just don't deal with the problems that I face on a regular basis.

I write a lot of games and game engine code. I might have 60 dynamic objects in the game, each of which with its own behaviors, state and physics. Object oriented code means that the functionality for each object has both inherited and specialized behavior.

And when you're working with those dynamic objects, the "information hiding" aspect of OO programming can be very useful. Some objects have a member variable; others have an accessor that pulls the value out of a child class. I use that to good effect in my current game.

Or I might be parsing a map that I need to iterate over a rectangle in the map. But sometimes it's a hex map; ever tried to iterate over a hex map? Saying map.iterateRect(iterator, x,y,w,h) or equivalent, and not having to know whether the map is rectangular or hexagonal is very nice.

Functional programming is great when you have a lot of generic functions that you can apply to lots of different kinds of data. Object oriented programming ... is good for UI and game development, and anything else where the functions you're writing are very tailored to the data, where polymorphism can map well to the problem, and where you have lots of variations of kinds of data, but fewer (or less complex, or more specialized) things to do with the data.

I think the point still stands: FP is different.

None of the things you listed are particularly hard in an FP context. Classes/traits and parametric polymorphism will allow you to do these things just fine (Haskell code):

    class RectIterable map where
      iterateRect :: map -> (Point -> a) -> Rectangle -> [a]

    // define map data structures
    data TileMap = ...
    data HexMap  = ...

    // make them instances of the RectIterable class
    instance RectIterable TileMap where
      iterateRect = ...

    instance RectIterable HexMap where
      iterateRect = ...
> Object oriented programming ... is good for UI

I don't know, but React begs to differ. A pure function (state -> UI) is basically one of the best UI programming paradigms we've come up with so far, the other probably being FRP.

> React begs to differ.

React is good for certain classes of UI, granted. I use another FRP-style library (not React) to render UI in my current game, and it works well -- except that it doesn't mesh well with me trying to control it from an OO game, but I've created an interface that works.

But imagine creating Adobe Photoshop, Maya, or Microsoft Word entirely in React, and tell me again that FP would be the ideal choice.

For any particular dialog that pops up? Absolutely.

For the entire app? You'd end up with a "god object" that would make the app unmaintainable. OO gives you compartmentalization that you don't get with FP.

FP is great for problems up to a certain complexity. After that it falls apart. Thing is, 98% of apps don't come close to that level of complexity, so there are lots of people who never even work on an app that won't work well with FP.

In the RectIterable example you gave, I'm afraid I don't know enough Haskell to comment on it intelligently.

Is "instance" adding an interface to TileMap/HexMap so that either can be passed in as a parameter that requires a RectIterable? Can other interfaces be added that way to TileMap/HexMap?

In my editor, can I say something like myTileMap.iterateRect after doing this, or do I need to know that iterateRect exists and remember exactly what it's called?

The last point is salient: Discoverability is a huge part of the UI of a programming language, and memorizing all of the exact function names that are appropriate for a data structure is not fun.

Oh, but you can have mutable state in different parts of a program even in a pure FP language (without having to jump through any hoops, it's right there and pretty easy to use), although a processor or signal simulator may benefit from things like FRP that are a lot better suited for, well FP, then imperative languages.

But I actually wasn't asking about pure vs imperative, but rather FP vs OOP. I haven't heard of a pure OOP language, but in that context I think those properties could be seen as more or less orthogonal.