Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Hello! Long-time lurker, and guilty dev behind the garage door. You can see the (broken) code I wrote here:

https://github.com/wpearse/wemos-d1-garage-door-wifi

I'll get around to fixing it later this week.

Also, an apology: I should have used "side-effect-free" instead of "idempotent" in my tweets.




> I should have used "side-effect-free" instead of "idempotent" in my tweets

The HTTP term is "safe method". Although you weren't even wrong because section 4.2.2 of RFC7231 (i.e. HTTP) defines all safe methods, including GET, as idempotent.

I think they use this language because nothing is truly side-effect free. In fact GETs can have side-effects, the most obvious of which is writing the fact of it to a logfile, and that's the most harmless side-effect of all, right until you run out of disk space.

Being a language arse I think the high precision descriptor is actually nullipotent. https://en.wiktionary.org/wiki/nullipotent but I'd never say it out loud.


'Side-effect free' means that doing it once, twice or n >= 3 times (with same parameters) yields the same result, i.e. what it returns doesn't depend on any remote state that is altered by the call itself.

However, an idempotent HTTP call is certainly not a pure function which some people seem to be mixing up. Pure functions don't work with I/O.

REST is bit more specific and explicitly requires GET to be nullipotent which really means "effect free" - it just reads and doesn't alter the state on the remote system at all.

Side-effects like log files, rate-limiting, etc. will always exist, but they do belong to a different 'layer', so to speak. That is, these should be unobservable side-effects (also think about minuscule effects on the power grid, the fact that a request might write something to an ARP-cache, etc. - they all happen at different layers, so the quantum world state keeps changing, but that's not what this is about). Whether an X-Request-Count header violates the requirements or not depends on interpretation. From the garage door perspective, I wouldn't care...


> Pure functions don't work with I/O.

there is a comment above that argue this point in better details.

https://news.ycombinator.com/item?id=16966046

Basically it depends on the many nuances you have on "pure", "functions" and "idempotent"


When discussing HTTP behaviours I think it’s easiest to stick to the definitions of these terms given in the HTTP standard. Anything else is fruitless bikeshedding.


IMO "side-effect free" is always a statement constrained by the operating level of abstraction. Logging is not an effect at the level of the application, but rather some subset of it's context (system, db).

I love that term and am going to use it as much as possible


Fixed now: https://github.com/wpearse/wemos-d1-garage-door-wifi/commit/...

I think.

edit: updated link because left creds in the commit :-/


I hope that's not your real password!


EDIT: it's actually his address, I thought it was just a coincidence but you can the house on Street View... I removed the actual name as doxxing isn't great, sorry.


Thanks. Not the address with the WiFi garage thankfully.

I was actually thinking "better not commit that" ... before, y'know, I committed it :/


I got just the thing for you:

https://gist.github.com/hraban/10c7f72ba6ec55247f2d

Every time you write some code you need to remember removing before commit, surround it with a comment containing "NOCOMMIT". With this script as a pre-commit hook, git will echo an error message and fail.

E.g.:

  print("debug: ", myval)
becomes:

  print("debug: ", myval) # NOCOMMIT
I end up relying on this every day I program. Can't go back.


Thanks! I'm not sure how easy it would be to put the git hook on all my machines though? I have a collection of laptops (and one desktop) that I work on and I often don't use the same machine for a few weeks :-/

I ended up using a "env.h" file... is there a C-equivalent of the PHP (?) .env file?


https://direnv.net Would be my recommendation


It's possible to remove that from the history of your repo, although it breaks any forks.

https://rtyley.github.io/bfg-repo-cleaner/


Heh. I think I did worse ... made a local copy of the repo, nuked it on GitHub, then re-created the three commits by hand ... less credentials.

That looks like a much more useful tool, though.


git add -p

It will let you approve each hunk in a file to commit or not.

git commit -e -v

Will force you to edit the commit message and in the editor show you the diff of the commit against HEAD.


Thank you! I use 'git add -p' all the time, but didn't know the trick with commit. I am a sucker for nice commits so I will check every commit's diff multiple times. When I don't, I usually end up including pieces of code which is not ready yet, which is meant for debugging,...


Well looks like a nice neighborhood at least.


Well according to Google it’s an address in Auckland NZ so I hope it’s not his address either.


hey, I'm in Auckland,maybe I can go around and hack his garage :)


Bring beer.


I kinda hope you come home to find multiple beers with notes saying "pull request pending"!


Me too!


Aw man I missed the address otherwise I would


Please, for the love of all that is holy, don't post long-form content on Twitter.


> Please, for the love of all that is holy, don't post on Twitter.

FTFY


I'm sorry. This is the first time I've used a thread. I won't do it again!


Actually, I think I actually wrote this story just so I could try out the "new" (months-old?) threading feature. I don't much twitter.


Anything side-effect-free is idempotent too, I suppose


No. A function which multiplies a number by two is side effect free, but is no idempotent.


I dispute your example. If you call f(2) and it always returns 4, it's idempotent and side-effect-free. If you call f() and it returns 4, then 8, etc, it is neither.


From wikipedia:

"A unary operation f, that is, a map from some set S into itself, is called idempotent if, for all x in S, f(f(x)) = f(x)."


Instead of checking wikipedia for a general definition of idempotency, check the RFC for the definition that applies to HTTP

9.1.2 Idempotent Methods

   Methods can also have the property of "idempotence" in that (aside
   from error or expiration issues) the side-effects of N > 0 identical
   requests is the same as for a single request.


Yes, in mathematics, not programming. And a function that doubles a number isn't idempotent even by that definition.


Of course a doubling function is not idempotent!

I think the confusion arises because side-effectful functions can be considered as having type

    f :: (RealWorld, OtherArgs) -> (RealWorld, OtherOutputs)
and so for a garage door toggle you have something like

    t :: RealWorld -> RealWorld
where the new state is the old one with the door opened/closed as appropriate.

Now the idempotence condition becomes:

    t(t(world)) == t(world)
but clearly

       t(t(doorOpenWorld)
    =  t(doorClosedWorld)
    =  doorOpenWorld
    != t(doorOpenWorld)
    =  doorclosedWorld
so this is where the notion comes from. If you abuse notation and just say a function of no arguments can be idempotent then you'll get confusion like this.


But `GET(GET(x))` doesn't make sense, in general (and if it did, then you would not expect it to be idempotent), so clearly idempotency in this context is meant to mean side-effect free. They should probably just say side-effect free, though, to avoid the confusion.


It's possible to be idempotent without being side-effect free. If you PUT some record, for example, then that operation _will_ have side effects (modifying the record). If you then PUT that same data again, the result will be the same (it's idempotent).


that’s not a side effect. in common usage, a side effect is an extra action, not the desired action itself. if you PUT some record, and some other record or state changes, that’s a side effect.


I don't disagree with common usage, but common usage is not what's being discussed here.

It is a side effect in programming usage (not just HTTP).

Something is side-effect-free if and only if the only result of it running is that you get an answer. If you ignore the answer, then you cannot tell you ran the function/method/call/whatever. PUT is not side-effect-free.

That said, side-effect-free-ness is an incomplete paraphrasing of the HTTP spec (RFC7231); you'll notice that the only mentions of the phrase "side effect" are giving examples of legal side effects.


I suppose logging is a side effect and that makes practically everything technically non-idempotent (though on purpose).


I think because in math you don’t ever have side effects you usually use composition where in programming you usually use a sequence. So to change that function in to how people would implement it means rearranging the internal stuff and then it probably wouldn’t be idempotent be either definition.


But not the reverse. If you are side effect free you must be idempotent.


"Idempotent" is one of those overloaded terms...


I think that is considered idempotent in the REST-sense of the word: you can multiply a number by two as many times as you like, the result will be the same (and no state is mutated).

I looked it up, apparently there's a formal definition "denoting an element of a set which is unchanged in value when multiplied or otherwise operated on by itself", which does not seem to describe the REST usage very well, though.


The typical operation applied to sets of functions is composition, so idempotency of a function f is the condition that f(f(x)) = f(x) for all x in the domain of f. I don't think that applies meaningfully to GET.


It does apply to HTTP idempotency. `x` is the state of the server. `f` is the change to the state of the server that ensues when one makes such-and-such an HTTP call. So taking PUT as an example, `x` is the state before the PUT, `f(x)` is the state after one PUT, and `f(f(x))` is the state after that single PUT is sent twice. Of course in a RFC7231-compliant server, `f(x) = f(f(x))`. Taking GET (or any other nullipotent method) as an example, we also see that in a RFC7231-compliant server, `x = f(x) = f(f(x))`.


Ah yeah that makes sense.


Technically speaking, idempotency as defined by RFC7231 only requires f(x) = f(f(x)).


Yes, and nullipotency requires x = f(x) = f(f(x)), and four of the methods defined in RFC7231 are expected to be nullipotent, otherwise known as "safe". The point of mentioning that is to highlight the relationship between idempotence and nullipotence.


Except if the domain is zero.


Or even a slightly more exotic case, such as Z mod 2 (a single binary digit)


No side effects means no garage door opening or closing. I guess you wanted it to open or close in some cases at least?


Isn't the point of this thread that the OT says that he shouldn't have used GET for this?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

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

Search: