Hacker News new | past | comments | ask | show | jobs | submit login

The usual implementation of fold/reduce takes a separate seed argument (as does clojure, optionally!), which IMO is far more sensible than having the same function do two completely separate things depending on the number of arguments.



Sure, let's roll with that.

How would you write a function parameter that takes two arguments, whose type is determined by the seed argument?

Also, with reduce, the return value of the function can determine what the type of the input arguments should be. For example if you pass in a `(fn (x y) (list x y))`, you end up with:

  > (reduce (fn (x y)
              (list x y))
            (list "a" "b" "c" "d"))
  ("a" ("b" ("c" "d")))
Let's throw in a print statement to print out the `y` parameter:

  > (reduce (fn (x y)
              (print (str y))
              (list x y))
            (list "a" "b" "c" "d"))
  "d"
  ("c" "d")
  ("b" ("c" "d"))
  ("a" ("b" ("c" "d")))
So `y` starts out as a string, then a list of strings, then a list whose first element is a string and the second element is a list of strings, and so on.

I'm not sure what the type of that function would even look like. And if there's no way to express something as simple as `reduce` without resorting to `f: (x: Any, y: Any) -> Any`, are we certain it's good design?


The type of the reduction function would just be something like

    (X, Y) -> (X Y)
     args     pair
The type of a reduce that has an initial seed is something like

     X   -> ((E, X) -> X) -> [E]  ->   X
    seed       reducer       list    result
The result is going to need to be some recursively defined type, which I'll call List,

    type List E = E | (E (List E))
Apply these together trivially

    (List E) -> ((E, (List E)) -> (List E))
             -> [E]
             -> (List E)


And this is the other thing I like about the haskell/ML collection of languages. The description you gave above is extremely concise and direct. If you're familiar with the language being used it's a very efficient form of communication.

I've noticed that working in languages of that family gives you a vocabulary to talk about things that previously would have been very wordy to discuss.

Languages with these types of type features provide easy abstractions to illuminate structure and patterns begin discussing new ideas that previously would've been considered on off code.


FWIW, this can be inferred fully automatically. Type

    let pair = fun x -> fun y -> { _0 = x; _1 = y }

    let rec reduce = fun init -> fun combine -> fun xs ->
        match xs with
            [] -> init
          | x :: rest -> reduce (combine x init) combine rest

    let test = reduce 0 pair [1; 2; 3; 4]
into https://www.cl.cam.ac.uk/~sd601/mlsub/ to see a demonstration.


> How would you write a function parameter that takes two arguments, whose type is determined by the seed argument?

I feel like I'm missing something here. I would think it would just be

    reduce<I, O>(fn:(O, I|O)=>O, seed:I): O


How would you call it? I'm interested in how you'd specify `I` and `O`.

`I` is obviously a string, but it seems like `O` needs to be at least three different types:

  > (reduce (fn (x y)
              (list x y))
            (list "a"))
  "a"
  > (reduce (fn (x y)
              (list x y))
            (list "a" "b"))
  ("a" "b")
  > (reduce (fn (x y)
              (list x y))
            (list "a" "b" "c"))
  ("a" ("b" "c"))


`O` would be whatever the type of the reducer is, so for `list` in the lisp you're using (is it Clojure? The function params look wrong) has a type signature `(A, B?, ...Z?) -> A | List(A, B, ...Z)`.

So the transducer in this case would have the type `List(A, B?, ...Z?) -> A | List(A, B | List(B, ...etc))`

It's not three different types, but it is necessarily recursive, which seems tricky.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: