Hacker Newsnew | past | comments | ask | show | jobs | submit | vq's commentslogin

One "feature of currying" in Haskell that isn't mentioned in the fine article is that parts of the function may not be dependent on the last argument(s) and only needs to be evaluated once over many application of the last argument(s) which can be very useful when partially applied functions are passed to higher-order functions.

Functions can be done explicitly written to do this or it can be achieved through compiler optimisation.


That's a very good point, I never thought really about how this relates to the execution model & graph reduction and such. Do you have an example of a function where this can make a difference? I might add something to the article about it.

It's also a question of whether this is exclusive to a curried definition or if such an optimization may also apply to partial application with a special operator like in the article. I think it could, but the compiler might need to do some extra work?


An example where this is useful is to help inline otherwise recursive functions, by writing the function to take some useful parameters first, then return a recursive function which takes the remaining parameters. This allows the function to be partially in-lined, resulting in better performance due to the specialization on the first parameters. For example, foldr:

foldr f z = go

  where

    go [] = z

    go (x : xs) = f x (go xs)
when called with (+) and 0 can be inlined to

go xs = case xs of

    [] -> 0

    (x : xs) = x + go xs
which doesn't have to create a closure to pass around the function and zero value, and can subsequently inline (+), etc.

One slightly contrived example would be if you had a function that returned the point of a set closest to another given point.

getClosest :: Set Point -> Point -> Point

You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important.

Another example would be if you were wrapping a C-library but were exposing a pure interface. Say you had to create some object and lock a mutex for the first argument but the second was safe. If this was a function intended to be passed to higher-order functions then you might avoid a lot of unnecessary lock contention.

You may be able to achieve something like this with optimisations of your explicit syntax, but argument order is relevant for this. I don't immediately see how it would be achieved without compiling a function for every permutation of the arguments.


I think we need to see a few non-contrived examples, because i think in every case where you might take advantage of currying like this, you actually want to make it explicit, as you say.

The flip side of your example is that people see a function signature like getClosest, and think it's fine to call it many times with a set and a point, and now you're building a fresh quadtree on each call. Making the staging explicit steers them away from this.


> and now you're building a fresh quadtree on each call [...] Making the staging explicit steers them away from this.

Irrespective of currying, this is a really interesting point - that the structure of an API should reflect its runtime resource requirements.


Consider a function like ‘match regex str’. While non-lazy languages may offer an alternate API for pre-compiling the regex to speed up matching, partial evaluation makes that unnecessary.

Those are nice examples, thanks.

I was imagining you might achieve this optimization by inlining the function. So if you have

  getClosest(points, p) = findInTree(buildTree(points), p)
And call it like

  myPoints = [...]
  map (getClosest(myPoints, $)) myPoints
Then the compiler might unfold the definition of getClosest and give you

  map (\p -> findInTree(buildTree(myPoints), p)) myPoints
Where it then notices the first part does not depend on p, and rewrite this to

  let tree = buildTree(myPoints) in map (\p -> findInTree(tree, p)) myPoints
Again, pretty contrived example. But maybe it could work.

I didn't consider inlining but I believe you're correct, you could regain the optimisation for this example since the function is non-recursive and the application is shallow. The GHC optimisation I had in mind is like the opposite of inlining, it factors out a common part out of a lambda expression that doesn't depend on the variable.

I don't believe inlining can take you to the exact same place though. Thinking about explicit INLINE pragmas, I envision that if you were to implement your partial function application sugar you would have to decide whether the output of your sugar is marked INLINE and either way you choose would be a compromise, right? The compromise with Haskell and curried functions today is that the programmer has to consider the order of arguments, it only works in one direction but on the other hand the optimisation is very dependable.


> explicitly written to do this

In that case I want the signature of "this function pre-computes, then returns another function" and "this function takes two arguments" to be different, to show intent.

> achieved through compiler optimisation

Haskell is different in that its evaluation ordering allows this. But in strict evaluation languages, this is much harder, or even forbidden by language semantics.

Here's what Yaron Minsky (an OCaml guy) has to say:

> starting from scratch, I’d avoid partial application as the default way of building multi-argument functions.

https://discuss.ocaml.org/t/reason-general-function-syntax-d...


It may or may not change everything.


only if you have a 28.8 bps [sic] modem!


and triple the RAM


Emacs is practically a product of AI research.

Even if it didn't, it's distributed with a psychotherapist mode, hippie-expand (which I use almost every day) and dabbrev.


I maintain a couple of Debian servers and this is how I do it too.

Reverse proxy, DB, etc from Debian. The application server is built and deployed with nix. The Python version (and all the dependencies) that runs the application server is the tagged one in my nix flake which is the same used in the development environment.

I make sure that PostgreSQL is never upgraded past what is available in the latest Debian stable on any on the dev machines.


Are you aware of Sioyek[0]? It's a PDF viewer with a fairly minimal UI and a focus on keyboard interaction.

[0]: https://sioyek.info


SumatraPDF: https://www.sumatrapdfreader.org/free-pdf-reader

No frills, super fast and small. Been using it on Windows for years.


I recently downloaded SumatraPDF to open a PDF on Windows XP. Glad they still host a release that works on it. :)


Need wine (or something stronger) to run it on Linux.


Thanks, I love finding little nuggets like this here.


Eh, it vendors an old version of mupdf. Very bad idea, considering that it's a C program/library handling a notoriously complex format often shared on the Internet.

Personally, I just use mupdf (which I sandbox through bubblewrap).


It reminded me of the Sinclair Zeta[0][1]. I hope their product work out better.

[0]: http://rk.nvg.ntnu.no/sinclair/vehicles/zeta.htm

[1]: https://www.grantsinclair.com/vehiclehistory


Clive Sinclair is mostly remembered for his computers but he was incredibly ahead of his time in the electric vehicle and personal transport industry.

Too ahead of his time, unfortunately, because the battery technology to support his vision just didn't exist, and consumer sentiment wasn't really there yet.

I'm sad he didn't get to see his visions come true.


I work almost exclusively in Emacs without the modern LSP-based tools. I believe I do keep more in my head than programmers that use more advanced IDEs.

In code I have control over myself I avoid imports that doesn't enumerate all imported symbols. That is I always use the import Library (symbol) syntax in Haskell and never do wildcard import in Python.

When coding C I sometimes use tags so that I can go to definitions quickly, I should probably use it more than I do to be honest.

I do use hippie-expand for quick auto-completion but it is completely textual, it has no understanding of the programming language it currently works on which makes it much less powerful of course but it also has some benefits.

I always have the documentation very reachable, I use a search keyword in my browser to search on hoogle. I type H <Space> symbol/type-expression <Enter> and I quickly find the documentation.

I do use Visual Studio on a Windows box for working on a C# codebase a couple of times per year and when I do I always turn off that code lens thing and I find that I rely on the code navigation features quite a bit. Part of it is probably due to it being a code base that is larger and that C#(/Java)-flavoured OOP makes the code more spread out. In terser languages like Haskell (which is much terser) it is natural for more functionality or types to live in the same file which means that you get much further with just simple textual search.


That was me for 7 years (using vim instead of Emacs), and it was working fine with Python and Go. I used ctags for navigation, some shortcuts for pydoc, and that was essentially it.

Then I started working with Scala and, until metals (the language server for Scala) and LSP support was good enough (first vim, now I'm a happy nvim user), it was awful.

So I'm certain it depends on the language. My take before Scala clicked for me was that I didn't want to use a language that required an IDE (or IDE-alike features) to be productive. And I think that opinion was mostly because my bad experience with Java.

I still write C without LSP, and I'm fine.


Despite many years of development, I find lsp and eglot to me mostly unusably slow. I need my emacs to be fast and the only way to achieve that is something oldschool like Jedi/Elpy for python.


> I work almost exclusively in Emacs without the modern LSP-based tools

I'm wondering how many people in the comments would misinterpret it as "Emacs is outdated"/"Emacs does not have modern LSP-based tools"


I was about to switch from Emacs when I found TIDE for Typescript development (which is what I do), and it kept me in Emacs for years longer.

Recently though I couldn't resist experimenting with Copilot and I switched to VS code for it, after 32 years. Is there a good Emacs module for it by now?


I don't know whether there is Copilot module for Emacs, sorry. I'm more of a fan of gptel approach https://github.com/karthink/gptel with explicit context and instructions.


There's definitely a lot of choices in Emacs land for these new LLM tools. There's copilot mode, chatgpt shell, gptel, theres so'e more from other AI startups. Plus writing LLM integrations for Emacs is a breeze with everything being text buffers.


I've been using copilot-mode in emacs for probably a couple of years.


I've not used copilot outside of emacs, but copilot-mode does everything I would want for code generation.


The reason I spelled it out is because I don't use them even though they are available. I realise now that what I wrote is ambiguous, thanks for mentioning it.


But that's bullshit. emacs has had most of the tools that LSP enables, it just had that only for some languages that had better emacs modes (C, elisp, Common Lisp etc.). All LSP is doing is making it easier to write modes for new languages really. emacs with eglot (not the LSP modes which are generally terrible) is a great IDE, and does thing the emacs way. Saying you use emacs but don't like "LSP" feels like a joke to me. The very reason SLIME was so great decades before IDEs became mainstream was that it was already a powerful IDE, it just does the IDE things a bit different than you're probably used to.


" I work almost exclusively using a drafting table and pencil without modern 3D CAD software"


My python+numpy+matplotlib version from when I was playing around with it:

  import numpy as np
  import matplotlib.pyplot as plt
  n = 2048
  X, Y = np.mgrid[:n,:n]
  plt.imshow(X^Y, cmap=plt.cm.Spectral)
  plt.show()


I've had luck with the Prologix Ethernet<->GPIB adapters[0]. At $500 I wouldn't call them cheap but they are a lot easier to integrate than those old NI/Keysight PCI/USB-based interfaces.

[0]: https://prologix.biz/product/gpib-ethernet-controller/


I live in Sweden where we have minimum 25 paid vacation days (I have 28) and 9 paid holidays.

https://en.wikipedia.org/wiki/List_of_minimum_annual_leave_b...


Sweden here also. Quite common with 25 days and paid overtime or 30 days with unpaid overtime.

And then you have parental days which are 480 in total per child which can be used both before they start preschool and for longer vacations when they are older. In Sweden it’s also quite common that both parents split it 50-50.

So 4-5 weeks of time off in the summer is not uncommon at all for parents and totally accepted by companies.


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

Search: