 # How to make a function factory

#1

I’m looking into rewriting parts of my VoronoiCells package. One thing I want to do is to have a type `Rectangle` and make functions that maps points between two Rectangles.

This attempt appears to work:

``````struct Rectangle
Left::Float64
Right::Float64
Lower::Float64
Upper::Float64
end

left(r::Rectangle) = r.Left
right(r::Rectangle) = r.Right
lower(r::Rectangle) = r.Lower
upper(r::Rectangle) = r.Upper

function construct_rectangle_map(from::Rectangle, to::Rectangle)
function rect_map(p::GeometricalPredicates.Point2D)
GeometricalPredicates.Point2D(
left(to) + (getx(p) - left(from)) / (right(from) - left(from)) * (right(to) - left(to)),
lower(to) + (gety(p) - lower(from)) / (upper(from) - lower(from)) * (upper(to) - lower(to))
)
end
end
``````

The `getx` and `gety` are from the GeometricalPredicates package.

With `from = Rectangle(-1, 1, 0, 1)` and `to = VoronoiCells.Rectangle(1, 2, 1, 2)` I get a map `m = construct_rectangle_map(from, to)` that seems to work: `m(Point2D(0.3, 0.4))`

However, looking at `@code_lowered m(Point2D(0.3, 0.4))` it appears that the the `left`/`right` are called inside the function.
I think it would be nice if the numbers are saved as the constants they are for a given map. Does anyone know how to do this?

Thanks!

#2

`left` and `right` will get inlined to struct accesses when it is compiled, but not to constants. In principle, there are ways to inline them to constants, but trying to have a separate compiled function for large numbers of rectangle pairs is likely to do more harm than good.

I would normally suggest just having a `rect_map(from, to, p)` function that takes 3 arguments. In any context where you need a single-argument function you can do `p -> rect_map(from, to, p)` … you don’t need a “function factory” pattern for this because `construct_rectangle_map` is neither shorter nor more convenient than `p -> ...`. (At some point in the future we might even have the syntax `rect_map(from, to, _)`.)

In the unlikely event that the cost of `rect_map` is the performance-critical step in your code, you can do further optimizations, but I would wait until profiling makes that clear.

5 Likes
#3

Thanks! I think your suggestion with a 3 argument `rect_map` sounds like a good plan.