5 ms·
If I'm writing a function where there isn't an obvious order for the arguments, I chose the order such that Currying is most useful, even if the language I'm wr
by KMag 12y ago
If I'm writing a function where there isn't an obvious order for the arguments, I chose the order such that Currying is most useful, even if the language I'm writing in doesn't support Curried functions/partial function application. You never know when someone will port your code or create cross-language bindings.
For instance, my company writes a lot of code in a proprietary language and the other day I was writing a function to generate random strings, given a length and an "alphabet" string from which to choose characters.
I chose the ordering:
randomString alphabet length = ...
so that if partial function application were added to the language, one could simply write the functions randomHex and randomBase32 as:
randomHex = randomString "0123456789ABCDEF"
randomBase32 = randomString "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
The alternative ordering would allow me to easily write functions that generate fixed length strings and take their alphabets as inputs:
random10Char = randomString 10
random4Char = randomString 4
This ordering is much less useful in general, so I chose to make the alphabet the first parameter.
On a side note, our language also has some time series extensions for dataflow programming. There's an API for efficiently adding new data flows based on something similar to template specialization, but the API is unwieldy and the source of much confusion. So, I wrote a wrapper API that uses reflection to expose the same functionality as partial function application / Curried functions over time series.
- CrossEye 12y agoI've done that too, then had to wave my hands about why I choose that parameter order, when I know damn well the languages isn't going to change... "But just in case...." :-)
- joeyh 12y agoIt's an interesting design question to pick orderings that work best with currying. Oddly though, when writing haskell code, I don't worry about it much. Once I find myself needing to `flip` a function before using it, I have determined experimentally that the original parameter order I picked was not the best one, and I then go change the function, and let the type checker walk me through fixing any necessary call sites (which also verifies if the new parameter order is better or not).
- KMag 12y agoDepending on the size of your codebase, and the human and financial costs of a mistake, code reviewers may need strong justifications for function signature changes. Also, in my case, the language is gradually typed, so the type checker helps a lot, but doesn't disprove the possibility of runtime type errors.
- mikeash 12y agoI wonder why languages don't support currying arbitrary parameters. It seems like it would be easy enough to have a special "placeholder" keyword that means "these parameters will be the parameters of the curried function". To take his example: var jp = formatNames2('John', 'Paul'); That might be written like this instead: var jp = formatNames2('John', 'Paul', _); And then if you want to curry the second parameter instead of the third, it's easy: var jp = formatNames2('John', _, 'Jones'); Or maybe they do and I just haven't heard of it?
- netheril96 12y agoWell, C++ has it. It is called std::placeholders::_1, std::placeholders::_2, ...
- malisper 12y agoClojure[0] and Anarki[1] (public fork of Arc) both provide a way to do this (with more than one argument as well). They both have syntax for a "literal function". Your second example would become: Arc/Anarki: [format-names2 "John" _ "Jones"] which expands to (fn (_) (format-names2 "John" _ "Jones")) Clojure: #(format-names2 "John" % "Jones") which expands to (fn (%) (format names2 "John" % "Jones")) Then for a function of two arguments that curried the middle one: Anarki: [format-names2 _1 "Paul" _2] which expands to (fn (_1 _2) (format-names2 _1 "Paul" _2)) Clojure: #(format-names2 %1 "Paul" %2) which expands to (fn [%1 %2] (format-names2 %1 "Paul" %2)) [0] http://clojure.org/reader http://clojure.org/reader [1] https://github.com/arclanguage/anarki/blob/master/CHANGES/make-br-fn https://github.com/arclanguage/anarki/blob/master/CHANGES/ma...
- 12y ago
- TheLoneWolfling 12y agoI have a Python function that allows both of those: def randomString(alphabet, length): .... randomHex = partialApply(randomString, alphabet="0123456789ABCDEF") randomBase32 = partialApply(randomString, "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") random10Char = partialApply(randomString, length=10) random4Char = partialApply(randomString, length=4) randomHexByte = partialApply(randomString, "0123456789ABCDEF", 2) Comes in handy occasionally.
- riwsky 12y agoAnd in case anyone else wants such a function, it's available in the standard library as functools.partial: https://docs.python.org/2/library/functools.html https://docs.python.org/2/library/functools.html
- TheLoneWolfling 12y agoEh, I don't like the one in functools. It allows overriding of variables previously defined, which personally causes more problems than it solves. (So `functools.partial(foo, b=3)(a=1, b=2)` doesn't throw an error, and is equivalent to calling `foo(a=1, b=2)`)
- CrossEye 12y agoI think languages that have named parameters have very different sorts of techniques for these things. I don't know if they're more powerful or not, but they take a very different approach, and offer different design choices.