In J, functions have inverses (2012)
prog21.dadgum.com
prog21.dadgum.com
math.sqrt(sum((i**2 for i in numbers)))
Using "with" to calculate a magnitude would be tortuous at best and it certainly wouldn't result in idiomatic code.The point of the article is to illustrate the idiom: perform operation A, then operation B, then the inverse of operation A. "with open(foo)" or "using (foo)" implements that idiom in certain, specific cases, but not in the general case which seems to be what J implements.
[0] Or "using" in C#, for that matter.
`with` implements this idiom just fine, too, but `under` is a more general solution that can solve even more problems, if you're willing to be a bit more careful about `f` and `g`.
In the Read/Open case, for example, it looks like Read must return both the read result and the file handle, and Open^-1 must take the read result as input and forward it back out as output. Feasible, but unintuitive for the implementers.
If we were actually implementing this resource management idiom, I'm not sure we would use `under`, even though we could. If we want to write `f`, `f^-1`, and `g` with the more natural `with` semantics, it's clearer to define our own metafunction from scratch: `with(f, g)(x) = (g(f_result), f^-1(f_result)) where f_result = f(x)`.
edit: The follow-up post actually touches on how Read/Open really isn't a great example of `under`. Interesting. http://prog21.dadgum.com/122.html
Even without the mapping wrinkle, though, this is definitely a deviation from the examples the article provides: instead of unsquaring the vector we initially squared, we unsquare the sum.
I think that your pseudo-code for `under` should start with `f^(-1)` (as in the fourth paragraph); probably `f^1` is just a typo.
bracket :: IO a -- computation to run first ("acquire resource")
-> (a -> IO b) -- computation to run last ("release resource")
-> (a -> IO c) -- computation to run in-between
-> IO c
And because of the order of parameters, one may write e.g. bracketReadFile :: FilePath -> (Handle -> IO c) -> IO c
bracketReadFile fname = bracket (openFile fname ReadMode) hClose
to specialize bracket to specific domains.Everything becomes neat, tidy, orthogonal, compositional.
The function bracketReadFile is itself a specialization of the library function:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
which also builds on top of bracket in the same way. See https://hackage.haskell.org/package/base-4.8.1.0/docs/src/Sy...(Yes, I'm perfectly aware of the wrinkles that Lazy I/O causes. Assume the above are the strict equivalents.)
http://hackage.haskell.org/package/lens-4.12.1/docs/Control-...
Context managers works well for files (or general relations on objects really) but less for more abstract operations, I've found.
import math
def sqr(x):
return x*x
class VecMag:
def __init__(self, comp):
self.comp = comp
def __enter__(self):
self.comp = map(sqr, self.comp)
return self
def __exit__(self, type, value, traceback):
print math.sqrt(self.mag)
def sum(self):
self.mag = sum(self.comp)
comp = (1,2,3,4)
with VecMag(comp) as squared:
squared.sum()
$python test.py
5.47722557505 File.open("foo.txt", 'w') {|f| f.write("bar")}Previously: https://news.ycombinator.com/item?id=3422654
Don't forget to read the followup: http://prog21.dadgum.com/122.html
(and yet, people are still misunderstanding it.)
(I do not advise actually doing this for anything besides being silly on HN)
R^~1 ; S ; R
where R and S are functions, ';' is composition ('.' in Haskell) and '^~1' is inverse. Usually this is for wiring (change order or grouping of wires to suit block S).
The wrinkle in Ruby is that R and S can be relations, not just functions, so data can flow both ways, just like actual hardware (can only flow from right to left in Haskell). Ruby won't try to guess the obverse function, unlike J.
S \ R
An example I've run into a lot is wanting a generalized "toggle" for DOM actions in javascript (for example, show/hide), so if some property is true, I apply a transformation, and when the property is switched off I can apply the reverse easily.
The trickiness with RAII (or things like context managers in python) is that it mainly works when you have a companion object representing the action (files are the companion object to getting handlers through opening/closing for example). But what is the object you could use for scaling?
You can, of course, build a Scaler object (or MultiplierFactory ;) ), but I find myself wanting to avoid objects and methods to instead go for plain functions as much as possible.
I don't think it's Turing completeness that's the issue (though it is an issue, of course) so much as non-invertibility. (I've always been fascinated by https://en.wikipedia.org/wiki/Reversible_computing , but it doesn't seem, at least to my limited perspective, like it's the subject of much current research.) For example, what should the inverse of `(^2) :: Complex -> Complex` be?
with open('workfile', 'r') as f:
read(f)Reading various responses and closer inspection of the article made me realize that it seems what is described is more along the lines of a Natural Transformation[1] where the functors are endomorphic[2].
1 - http://www.worldwizzy.com/library/Natural_transformation
[1] http://www.cis.upenn.edu/~bcpierce/papers/lenses-etapsslides...
[2] http://www.janis-voigtlaender.eu/papers/ThreeComplementaryAp...
It's almost like a marriage of Applicative Functor[1] with Lens in a squint-your-eyes-and-tilt-your-head kind of way.
µ : F(x) -> G(x)
such that for all (f : x -> y), (F(f);µ == µ;G(f)). I don't see how that's capturing anything interesting here. If µ were a natural isomorphism with inverse µ- then there'd be laws like (F(f) == µ;G(f);µ-), but this exactly involves introducing an inverse so the natural transformation didn't buy much.http://edgeguides.rubyonrails.org/active_record_migrations.h...
A framework agnostic tool for addressing this concern is:
https://mybatis.github.io/migrations/
For managing RDBMS schemas, it is very impressive IMHO.
I'm not a J expert; does anyone know if there are any other functions like this in the language?
It seems like perhaps a more pragmatic example of this syntax is the "with" syntax in python, which provides the same benefit with more flexibility.
Example: a*b = exp(log(a)+log(b)).
There are many diagrams in math fields that show exactly this.
F(X) = X%2
This is hardly a new concept.
This is really more like map-reduce, with an additional inverse step deduced from the inverse of the function that is used for the map step.
With map-reduce, we can map all the inputs individually through a square function, and then reduce with +. Okay, that brings us to sum of squares. "Under" adds one more step: to "undo" the squaring with a square root, and without being explicitly given the square root function.
If we have a zoo of functions that are all associated with inverses, we can roll this into a single operation. Say a functional combinator under(T, P) which takes a processing function P (which takes one or more arguments) and a transforming function T (one argument), and produces a new function F which is of the same arity as P, such that F(a, b, c, ...) calculates T_inverse(P(T(a), T(b), T(c), ...)).
The we have under(square, sum)(1, 2, 3) to take the norm of 1, 2 and 3.
- with blah do x
- using blah do x
- try(blah) { do x }
- File.open(blah, 'w') {|f| f.write(x) }
- there are even GCC C complier extension to hook in behavior like this
It's all over the place. Everything is new and old at the same timeEven the specific example he gives is NOT about recourse management (which is what the things you mention do).