Live data from Hacker News

Currying

wiki.haskell.org

121–130 of 134 posts

Re: Currying

#121
post #21

Earlier quoted context omitted.

I much prefer partial application. It just makes more intuitive sense - you take a function with many arguments, "fix" some of those arguments, and get a new function out. No need to mess with argument ordering, or all that. It's a shame the only language that did partial application well is, weirdly, Python.

In Gleam it also seems to be done well: https://tour.gleam.run/functions/function-captures/

Can I have that in Rust? :D

Re: Currying

#122

Earlier quoted context omitted.

We can say that when a function is curried (i.e. prepared or converted to curried form) then it supports partial application without any further transformation. In fact it happens naturally whenever the function is invoked. E.g. if we have (lambda (x y z) (+ x y z)) converted into the curried form (lambda (x) (lambda (y) (lambda (z) (+ x y z)))), then while we do not yet have partial application, we can then partiall…

I think when calling a curried function it isn't easy to differentiate between a simple function application and a partial (function) application . I would make the assumption that as long as a higher-order function is called, then what we're actually doing is a function transformation and thus a partial application - when we applied all arguments except the last one, then finally, when the last argument of a first-o…

[deleted]

Re: Currying

#123

Earlier quoted context omitted.

> I will get a type error and it will take me 2-3 seconds to figure out what it is about. You should really screen capture yourself coding sometime. On a large codebase you're lucky if the compiler even runs in 10 seconds. > Why would it be in a completely different part of the code? At most it would be 2 lines away, but usually on the same line. Because possibly you return the partial assuming it's a number, and try…

> Because possibly you return the partial assuming it's a number, and try to use it somewhere else, possibly even in another file. Sorry, I must've been more explicit instead of implying certain usage patterns. What I meant here is that I have a hard time imagining this happening because I would start working on a function by writing its type signature. Unless my types check out, I won't be able to mark this function…

Sure, but that usage pattern is not without cost. You're basically eschewing relying on type inference across function boundaries.

I think writing out type signatures is The Right Way, but it does take time, and The Right Way doesn't always happen on complicated projects with close deadlines.

Re: Currying

#124

Earlier quoted context omitted.

> It isn't true and i can understand why someone would make that statement It is true, and you are smart enough to understand it if you stop assuming you're the only one who understands the topic. I'm specifically sticking with the wording "Currying is a subset of partial application" to make the point that you can understand what someone is trying to say even if they don't say it exactly the way you would like them…

Neither of your examples does curry a function. The first one partially applies it (the Haskell way), the second one wraps the partial application in a redundant lambda (Haskell again). I think that's where the confusion is, let me also try to use the Haskell syntax... This is an add function with two arguments: add :: (Int, Int) -> Int add (x, y) = x + y At the moment it can not be partially applied, so let's curry…

Coming back to this having spent a wonderful day outdoors.

Okay, writing this in Python because a) it's been a while since I wrote Haskell, b) it will let other readers read the code more easily, and c) Python clearly denotes what a partial is by having a `partial` function:

    from inspect import signature
    from functools import partial

    def curry(f, parameters=None):
        if parameters is None:
            parameters = list(signature(f).parameters.keys())

        if len(parameters) == 0:
            return f

        head = parameters[0]
        tail = parameters[1:]

        # I draw your attention to this line
        return lambda p: curry(partial(f, **{head: p}), tail)

    def divide(a, b, c):
        return a / b / c

    curried_divide = curry(divide)

    # prints "3"
    print(curried_divide(30)(5)(2)())
So... curry can be implemented in terms of partial applications, no?

Re: Currying

#125

Earlier quoted context omitted.

They say that array languages are pretty readable once you get used to them, too? But you’re drastically limiting your audience. Part of readability is writing for people who aren’t as fluent as you are. Expert jargon can sometimes be useful, but it often obscures things that would be pretty simple if written some other way. With Haskell there’s a tension between saying “I only care about writing for other expert pro…

It's not really saying "only expert programmers" though, is it? It's people who know Haskell, which by coincidence happens to be overzealous undergraduates and a certain subset of experienced programmers. FP is a paradigm among many, its basics somewhat predate (or since it's so close, co-date?) more imperative descriptions of computation. That we mostly use and as such mostly teach beginners with procedural language…

I guess my bigger complaint in this example is that there's a lot left out. What's in `symbols`? What is each `coord`? What does `neighbouringNumbers` do? What is this function trying to do?

I write Python a great deal for a living these days and the Python code isn't much clearer to me. In both the Python and Haskell examples I can tell what it's doing (except the opaque neighbouringNumbers)--I just can't tell why it's doing it.

Re: Currying

#126

I recently saw the title, Learn Physics with Functional Programming, and thought to myself... I know physics and functional programming but I want to learn Haskell. Having gone through it, I highly recommend the book; especially to anyone knowledgeable about any 2 and interested in the third. I love Haskell. I love writing it, and reading it. Haskell is a beautiful programming language. One where I felt immediately a…

Wow what a lot of parentheses! To me the first example is perfectly clear because it follows the same precedence rules I learned for arithmetic at school, along with most programming languages. There's nothing special about Haskell here, in Python I would write: >>> math.sqrt(2) + 1 * 3 + 3 * 2 + 1 / 7 10.557070705230238 The only difference I see is the parentheses used for function application in python.

You should see when negative numbers are involved.

Due to Haskell's rigor in regard negative numbers:

    ghci> -1 + 2
    1
    ghci> 2 + -1
    :12:1: error:
    Precedence parsing error
        cannot mix ‘+’ [infixl 6] and prefix `-' [infixl 6] in the same infix expression
    ghci> 2 + (-1)
    1
    ghci> -1 * 3
    -3
    ghci> 3^-1
    :3:2: error:
    • Variable not in scope: (^-) :: t0 -> t1 -> t
    • Perhaps you meant one of these:
        ‘^’ (imported from Prelude), ‘-’ (imported from Prelude),
        ‘^^’ (imported from Prelude)
    ghci> 3^^-1
    :4:2: error:
    • Variable not in scope: (^^-) :: t0 -> t1 -> t
    • Perhaps you meant ‘^^’ (imported from Prelude)
    ghci> 3**-1
    :5:2: error:
    • Variable not in scope: (**-) :: t0 -> t1 -> t
    • Perhaps you meant ‘**’ (imported from Prelude)
    ghci> 3**(-1)
    0.3333333333333333
    
    ghci> sqrt 2 + 1 * 3 + 3 * 2 + 1 / 7
    10.557070705230238
    ghci> sqrt $ 2  + 1 * 3 + 3 * 2 + 1 / 7
    3.3380918415851206
    ghci> -sqrt 2  + 1 * 3 + 3 * 2 + 1 / 7
    7.728643580484048
    ghci> -sqrt $ 2  + 1 * 3 + 3 * 2 + 1 / 7
    :25:1: error:
    • Non type-variable argument in the constraint: Num (a -> a)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall {a}. (Floating a, Num (a -> a)) => a

    ghci> sin -1
    :7:1: error:
    • Non type-variable argument in the constraint: Num (a -> a)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall {a}. (Floating a, Num (a -> a)) => a -> a
    ghci> (sin -1)
    :10:1: error:
    • Non type-variable argument in the constraint: Num (a -> a)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall {a}. (Floating a, Num (a -> a)) => a -> a
    ghci> sin (-1)  -- -0.8414709848078965
Let's see,

    ghci> (sin (-1))  -- -0.8414709848078965
Ahh, now that's better.

Re: Currying

#127

Earlier quoted context omitted.

It's not really saying "only expert programmers" though, is it? It's people who know Haskell, which by coincidence happens to be overzealous undergraduates and a certain subset of experienced programmers. FP is a paradigm among many, its basics somewhat predate (or since it's so close, co-date?) more imperative descriptions of computation. That we mostly use and as such mostly teach beginners with procedural language…

It’s true that readability is culture-specific, that if the culture were different and people learned different things then different languages would be more readable. But I still think there are differences between languages in the sense of how much you can understand without knowing the definition of every term. For example, if you’re looking at Lisp code and you don’t know whether an outer term is a function, macr…

> Macros aren’t marked, so any unknown term might be a macro.

Yes, that's a problem.

There are two clues:

* Macros typically have naming conventions. For example anything beginning with DEF should be a defining macro, not a function. Anything with DO- will be a control structure. Anything with WITH- will be a scoping macro. And so on. Avoid active names like CREATE- and use DEFINE- instead. CREATE-CLASS would be a function, DEFINE-CLASS would be a macro.

* In source code the enclosed code typically has syntax and special indentation, when used with macros. Functions have certain uniform indentation rules.

    (create-class 'container-ship
                  :superclasses '(ship)
                  :slots '((max-number-of-containers :type integer))
                  :documentation "our new container-ship class")
Above is a function and uses one of the typical formatting/indentation rules for functions. Line up the arguments. Start with the required argument and then the named argument pairs (name, arg).

The macro looks different. The first important things like name and superclasses are on the first line. The other parts of the class specification follow and are indented by two characters.

    (define-class container-ship (ship)
      ((max-number-of-containers :type integer))
      (:documentation "our new container-ship class"))
Developers who write macros should make it clear what a macro is, by using hints like these.

Re: Currying

#128

Earlier quoted context omitted.

> no one in this comment chain is confused about what currying or partial application are I can't agree here either... I know it from my own experience when i first learned about currying and partial application.

It does not appear to me that skybrian is first learning about currying and partial application, and I'm not first learning about currying or partial application in this thread either. Your play at humility that you didn't understand currying and partial application in the past kinda falls flat if you then go on to less-humbly assume that everyone else in the conversation is stuck where you once were.

You seem to be reacting to your own assumption that the original response was making an assumption.

I understood it instead to be generalizing to a related concept, and inferred no condescension, FWIW.

Re: Currying

#129
I understand currying to be using a closure to "bake in" a value for a parameter.

Reading through these comments, it's clear that currying isn't clearly understood, which makes me doubt it's worth the cognitive load to use in a codebase.

Post reply on HN