I have recently seen that there is a macro defined in base/c.jl called ccallable, but it is not clear to me what its usefulness is. It seems to be undocumented.
After reading the documentation a little more carefully, I would summarise the usefulness of this macro as follows:
Make the annotated function be callable from C using its name. This can, for example, be used to expose functionality as a C-API when creating a custom Julia sysimage.
Related
I'm now watching the talk Engineering Julia for Speed given by Lionel Zoubritzky on JuliaCon 2018. The talked mentioned a function called removetype at about 13:24. I'm wondering how that function could be defined? Thanks.
This is a relatively old video, so I am not sure which Julia version is used there, but currently this function is Base.inferencebarrier (at least as of Julia 1.6 - since it is not exported it is not part of a public API and should not be considered guaranteed to be defined in the future).
Fortunately it is easy enough to define it yourself (I am copy-pasting the current definition):
inferencebarrier(#nospecialize(x)) = Ref{Any}(x)[]
The function makes sure that the compiler is not able to perform type inference of its return type. It is mostly useful if you want to avoid excessive recompilation of your code (the biggest benefit is for cheap higher-order functions).
I know that from here:
Julia function arguments follow a convention sometimes called "pass-by-sharing", which means that values are not copied when they are passed to functions. Function arguments themselves act as new variable bindings (new locations that can refer to values), but the values they refer to are identical to the passed values. Modifications to mutable values (such as Arrays) made within a function will be visible to the caller. This is the same behavior found in Scheme, most Lisps, Python, Ruby and Perl, among other dynamic languages.
Given this, it's clear to me that to pass by reference, all you need to do is have a mutable type that you pass into a function and edit.
My question then becomes, how can I clearly distinguish between pass by value and pass by reference? Does anyone have an example that shows a function being called twice; once with pass by reference, and once with pass by value?
I saw this post which alludes to some similar ideas, but it did not fully answer my question.
In Julia, functions always have pass-by-sharing argument-passing behavior:
https://docs.julialang.org/en/v1/manual/functions/
This argument-passing convention is also used in most general purpose dynamic programming languages, including various Lisps, Python, Perl and Ruby. A good and useful description can be found here:
https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing
In short, pass-by-sharing works like pass-by-reference but you cannot change which value a binding in the calling scope refers to by reassigning to an argument in the function being called—if you reassign an argument, the binding in the caller is unchanged. This means that in general you cannot use functions to change bindings, such as for example to swap to variables. (Macros can, however, modify bindings in the caller.) In particular, if a variable in the caller refers to an immutable value like an integer or a floating-point number, its value cannot be changed by a function call since which object the variable refers to cannot be changed by a function call and the value itself cannot be modified as it is immutable.
If you want to have something like R or Matlab pass by value behavior, you need to explicitly create a copy of the argument before modifying it. This is precisely what R and Matlab do when an argument is passed in a modified and an external reference to the argument remains. In Julia it must be done explicitly by the programmer rather than being done automatically by the system. A downside is that the system can sometimes know that no copy is required (no external references remain) when the programmer cannot generally know this. That ability, however, is deeply tied with the reference counting garbage collections technique, which is not used by Julia due to performance considerations.
By convention, functions which mutate the contents of an argument have a ! postfix (e.g., sort v/s sort!).
I am porting a library to Julia and am curious what would be considered best practice for adding Base functions from a module.
This library contains functions like values(t::MyType) and keys(t::MyType) that take unique struct types but do not really do the same thing or return the same types as the Base functions
What would be the best practice in this case?
Just add Base.values(t::MyType) and Base.keys(t::MyType) functions so they can be used without prefixes.
Change the function names to my_type_keys(t::MyType) and my_type_values(t::MyType)
Use their original names and require them to be prefixed as MyModule.values(t) and MyModule.keys(t)
If you extend Base functions you should aim for them to do conceptually the same thing. Also, you should only extend Base functions to dispatch on types you define in your own package. The rule is that you can define methods for outside (e.g. Base or some other package's) functions for your own types. Or define you own functions for outside types. But defining methods for outside functions on outside types is "type piracy".
Given that you'd be defining them on your own types, it's not a problem that the return values differ, as long as the functions are conceptualy the same.
With regards to option 2 or 3 you can do both. Option 3 requires that you don't import the Base functions explicitly (in that case you'd be extending them by defining new methods rather than defining new functions with the same name), that you don't export them, and most likely that you'll not be using the Base functions inside your module (keys is really widely used for example - you can use it but would have to preface it with Base. inside the module).
Option 2 is always safe, especially if you can find a better name than my_keys. BUT I think it is preferable to use the same name IF the function is doing conceptually the same thing, as it simplifies the user experience a lot. You can assume most users will know the Base functions and try them out if that's intuitive.
There are probably several ways to implement this introspection feature through macros and code walkers, but is there a simpler (possible, implementation-dependent) way? I'd imagine, invoking and then releasing the debugger could open access to frame stack, but that seems like an overkill too.
What would be some simpler ideas to try?
Macros can take an &env argument that passes in the lexical environment of the calling context. You can then query the lexical environment using these functions: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node102.html
In particular, check out variable-information and function-information.
I believe there are also implementation-specific ways to get the current lexical environment at run-time.
Is it a way to create mutable state with modules? How can using this be a good idea? Wouldn't that kind of break the immutability idea from functional programming?
No because it's used at compile-time. It's kind of #define in C.
You can see example https://gist.github.com/mprymek/8379066 where attribute "sensors" is used to accumulate functions defined with macro "sensor". When you have all these functions accumulated, you can automatically generate function "run_all" which runs all of them. Of course all of this must be done at compile-time.