Live data from Hacker News

Functional Programming in C++ (2013)

web.archive.org

51–60 of 64 posts

Re: Functional Programming in C++ (2013)

#51
post #43

Earlier quoted context omitted.

The monad type-class operations carry the context around for you. You can just write in imperative-seeming do-notation. The difficult part is trying to combine monads.

Here's what I don't get with Monads, why I've not felt the need to create a Monad-like system in my (non Haskell) code. The 'world' is carried on the back of one parameter, right? So if I want to run a function taking two parameters in a particular 'world', which parameter is the Monad? If both of them are, what if they disagree which world is used? Perhaps I haven't reached the fabled epiphany of monads, but they se…

In a language with immutable values and pure functions, roll always returns the same number from the same generator. If you want to call roll twice and get different numbers, the first call needs to return a new generator (with an updated seed) to pass to the second call. Rather than having to write something like

  do
    (x, g2) 
it's convenient to have plumbing that hides all those single-use g values.

Re: Functional Programming in C++ (2013)

#52
post #46
post #3

Earlier quoted context omitted.

Exhausting is being in this situation: object Foo { var x: Int = 0 def bar(y: Int) = x + y } And trying to figure out what bar will return at any given point.

Are you somehow not in control of how x is being changed? You seem to have said that literally any statefulness is "exhausting".

What convinced me that avoiding mutations as much as possible was having to deal with code like that (in java):

    public class MyBigClass() {
        private int a;
        private int b;

        //hundreds of lines of code

        private int calculateSomething() {
            //do something
            a = a+1;
            return b*b;
        }

        //hundreds lines of code

        private int calculateSomethingElse() {
            int c = calculateSomething();
            return a+c;
        }

        //hundreds of lines of code
        public returnSomething() {
            return calculateSomethingElse();
        } 
    }
In this example. It's hard because you have to remember constantly which functions are changing your classes before you use them, and if you haven't written the functions, it's very easy to introduce a bug by involuntarily modify the states using the function calculateSomething, just wanting its output and not the modification of a.

Re: Functional Programming in C++ (2013)

#53
post #43

Earlier quoted context omitted.

Here's what I don't get with Monads, why I've not felt the need to create a Monad-like system in my (non Haskell) code. The 'world' is carried on the back of one parameter, right? So if I want to run a function taking two parameters in a particular 'world', which parameter is the Monad? If both of them are, what if they disagree which world is used? Perhaps I haven't reached the fabled epiphany of monads, but they se…

In a language with immutable values and pure functions, roll always returns the same number from the same generator. If you want to call roll twice and get different numbers, the first call needs to return a new generator (with an updated seed) to pass to the second call. Rather than having to write something like do (x, g2) it's convenient to have plumbing that hides all those single-use g values.

Thanks. So the issue is that Monads save us having multiple returns? That makes sense.

Re: Functional Programming in C++ (2013)

#54
post #53

Earlier quoted context omitted.

In a language with immutable values and pure functions, roll always returns the same number from the same generator. If you want to call roll twice and get different numbers, the first call needs to return a new generator (with an updated seed) to pass to the second call. Rather than having to write something like do (x, g2) it's convenient to have plumbing that hides all those single-use g values.

Thanks. So the issue is that Monads save us having multiple returns? That makes sense.

That's one of the things monads do, but they can do more. A monad is defined by an underlying type (roughly, some kind of "container"), and two functions (putting an object in the container, and creating a new container by applying a function to the contained object). That's all a monad actually is. It simply turns out that given those relatively simply building blocks, you can do all sorts of neato things with them, like encapsulating state, error handling, chaining actions together, etc.

An example of a monad which has really nothing necessarily to do with mutable state are Promises in JavaScript. The container, of course, is a Promise, and the two functions I described are `resolve` and `then`.

Re: Functional Programming in C++ (2013)

#55
post #53

Earlier quoted context omitted.

Thanks. So the issue is that Monads save us having multiple returns? That makes sense.

That's one of the things monads do, but they can do more. A monad is defined by an underlying type (roughly, some kind of "container"), and two functions (putting an object in the container, and creating a new container by applying a function to the contained object). That's all a monad actually is . It simply turns out that given those relatively simply building blocks, you can do all sorts of neato things with them…

Right, and some of that makes sense to me. It makes sense in some cases (Maybe was the example I gave, a Promised value another) to tie such things directly to a single typed value. But what I was querying was, is this implicitness a good move in general.

In particular the issue I was addressing was the encapsulation of an arbitrary state with a single typed value. It's been remarked elsewhere that 'mixing' monads is a problem: I guess that's always been the thing I've felt haven't made them work replicating as a coding pattern. I tend to prefer explicit over implicit state.

Re: Functional Programming in C++ (2013)

#56
post #55

Earlier quoted context omitted.

That's one of the things monads do, but they can do more. A monad is defined by an underlying type (roughly, some kind of "container"), and two functions (putting an object in the container, and creating a new container by applying a function to the contained object). That's all a monad actually is . It simply turns out that given those relatively simply building blocks, you can do all sorts of neato things with them…

Right, and some of that makes sense to me. It makes sense in some cases (Maybe was the example I gave, a Promised value another) to tie such things directly to a single typed value. But what I was querying was, is this implicitness a good move in general. In particular the issue I was addressing was the encapsulation of an arbitrary state with a single typed value. It's been remarked elsewhere that 'mixing' monads is…

> I tend to prefer explicit over implicit state.

I guess it depends on your definitions, but monadic state is just as explicit as any other.

    myMonadicFunction :: String -> State Int String
    myMonadicFunction string = do
      currentVal 
It's just as clear as what you'd have in an imperative programming language.

I'm convinced the main reason monads aren't common in other languages is that most languages just don't have strong enough type systems to support them explicitly. Implicitly they can be found here or there, such as with promises, or just the fact that the semantics of many imperative languages can be viewed as a monad. But of course, probably the biggest reason is that people who are used to mutable state don't see the need. It's one of those things whose value becomes apparent with use.

Re: Functional Programming in C++ (2013)

#57

One difficulty with functional style c++ is just how verbose it is. Every time I tried to use std::transform (the equivalent of "map") I had to delete it and rewrite as a loop because it was just too hard to read. Functional style on a large scale is still possible.

Much the those issues are addressed in various boost libraries. There is one which wraps all the algorithms and containers in range classes so that all the f(c.begin(), c.end(), g) silliness gets replaced with f(c, g). There is another one which makes it easy to create streams of transforming and mapping functions with a F1 -> F2 -> F3 interface.

Re: Functional Programming in C++ (2013)

#58
post #55

Earlier quoted context omitted.

Right, and some of that makes sense to me. It makes sense in some cases (Maybe was the example I gave, a Promised value another) to tie such things directly to a single typed value. But what I was querying was, is this implicitness a good move in general. In particular the issue I was addressing was the encapsulation of an arbitrary state with a single typed value. It's been remarked elsewhere that 'mixing' monads is…

> I tend to prefer explicit over implicit state. I guess it depends on your definitions, but monadic state is just as explicit as any other. myMonadicFunction :: String -> State Int String myMonadicFunction string = do currentVal It's just as clear as what you'd have in an imperative programming language. I'm convinced the main reason monads aren't common in other languages is that most languages just don't have stro…

Thanks, I really appreciate your responses.

What's to stop you passing a struct or object with the two monadic functions attached, or ignore the function requirement (for the case of state) and pass the struct with the state and result in it? The benefit being that you can package up more than just one value in that (immutable) structure along with the 'specialness'. I don't see why that is less typesafe.

Re: Functional Programming in C++ (2013)

#59
post #58

Earlier quoted context omitted.

> I tend to prefer explicit over implicit state. I guess it depends on your definitions, but monadic state is just as explicit as any other. myMonadicFunction :: String -> State Int String myMonadicFunction string = do currentVal It's just as clear as what you'd have in an imperative programming language. I'm convinced the main reason monads aren't common in other languages is that most languages just don't have stro…

Thanks, I really appreciate your responses. What's to stop you passing a struct or object with the two monadic functions attached, or ignore the function requirement (for the case of state) and pass the struct with the state and result in it? The benefit being that you can package up more than just one value in that (immutable) structure along with the 'specialness'. I don't see why that is less typesafe.

I'm having a tough time trying to understand what you're proposing. First of all, in Haskell there's no real concept of having functions "attached" to objects (in the OO sense). The closest analogue is a type class, which relates a type to a set of functions. For monad, that looks like

    class Monad m where
      return :: a -> m a
      (>>=)  :: m a -> (a -> m b) -> m b
And it's up to the particular instance to determine how those functions are implemented. For example,

    instance Monad Maybe where
      return :: a -> Maybe a
      return x = Just x

      (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
      maybe >>= f = case maybe of 
        Nothing -> Nothing
        Just x -> f x
The definition of the State type, and its corresponding Monad instance, is a little more involved (I suggest this blog post for an explanation [1]):

    newtype State s a = State { runState :: s -> (a, s) }

    instance Monad (State a) where
    return x = State $ \s -> (x, s)
    m >>= k = State $ \s -> let (a, s') = runState m s
                            in runState (k a) s'
I'm honestly not quite sure what you mean by the rest of it (for example, "ignoring the function requirement", "specialness" etc). But the bottom line is that you can do any typesafe thing you want to, within the rules of how the language works.

[1]: http://brandon.si/code/the-state-monad-a-tutorial-for-the-co...

Re: Functional Programming in C++ (2013)

#60
post #58

Earlier quoted context omitted.

Thanks, I really appreciate your responses. What's to stop you passing a struct or object with the two monadic functions attached, or ignore the function requirement (for the case of state) and pass the struct with the state and result in it? The benefit being that you can package up more than just one value in that (immutable) structure along with the 'specialness'. I don't see why that is less typesafe.

I'm having a tough time trying to understand what you're proposing. First of all, in Haskell there's no real concept of having functions "attached" to objects (in the OO sense). The closest analogue is a type class , which relates a type to a set of functions. For monad, that looks like class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b And it's up to the particular instance to determine how tho…

Thanks again.

Sorry, I wasn't talking about Haskell. Following the OP I was talking about the merits of using these concepts or structures in other languages (C++, as per the OP, in particular). Of course, in a language with strong support and syntax for a particular pattern, you should rely on that pattern. But beyond that, it can be useful to use a pattern in other languages. Carmack is talking about writing C++ in a more functional style. I've written OO plain C, for example, when that pattern has made sense, and much of my C++ is heavily influenced by my experience with Scheme.

My point was that I don't get the emphasis on Monads, as a concept, for the range of things it gets emphasised for. In a language which has core syntactic and semantic support, and there are few alternatives, then of course it has to be used widely. But that's a different argument to saying it is an intrinsically good solution for those cases. I might be missing something, but as an architectural pattern in general it doesn't seem appealing. Not enough to reimplement in another language.

That's the context to my comment on typesafety. You said that other languages aren't typesafe enough. I was suggesting that, implementing a monad as a structure or an object would be typesafe, so I couldn't see what was insufficient.

Post reply on HN