Tried this but no luck.
julia> (+!)(x, y) = 0
ERROR: syntax: invalid function name "+(!)" around REPL[35]:1
or
julia> function +!(x, y) = 0 end
ERROR: syntax: expected "end" in definition of function "+"
The idea is to apply it to two custom types and add them in situ.
Thanks
I guess for numbers or arrays the following would work:
a .+= b
So maybe you want to define some custom broadcasting rules for your types? See here: Interfaces · The Julia Language
You can use Meta.@dump
to get an insight into how the parser interprets +!
:
julia> Meta.@dump(1 +! 2)
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol !
2: Int64 2
We see that 1 +! 2
is just equivalent to +(1, !(2))
, so we can’t use it as an infix operator, or in fact as any valid identifier. Another way to check this are Base.isoperator
and Base.isidentifier
:
julia> Base.isoperator("+!")
false
julia> Base.isidentifier("+!")
false
You can use arbitrary names as identifiers with the var
string literal, like var"+!"
, but I don’t know if that’s what you are after.
5 Likes
You can of course be sneaky, but it’s a bit of an ugly solution, because you are actually overloading !
:
julia> struct A end
julia> struct B end
julia> struct Not
x::B
end
julia> Base.:!(x::B) = Not(x)
julia> Base.:+(::A, ::Not) = print("Boo!")
julia> A() +! B()
Boo!
2 Likes
I could live with the ugly but the problem is that I would also want -!, *!, /!, ^! ...
That’s easy, you can just overload Base.:-(::A, ::Not)
, Base.:*(::A, ::Not)
, etc. Just keep in mind that this only really works if you actually own the type on the right.
1 Like
Yes, this is to be applied to my own types.
Thanks, will try it.
1 Like
If you are trying to define in-place versions of elementary operations like +
, presumably because you are working with large data structures and want to avoid heap allocations, then realize that it is usually much more efficient to combine multiple simple operations in a single “fused” operation (for cache locality and other reasons), so you might want to re-think your approach.
This is precisely the reason for Julia’s fused broadcasting semantics: @. x = x * y - 3x + 2/z
fuses into a single loop acting in-place on x
for arrays x
,y
,z
. It is perfectly possible to use this syntax to define “fused” operations on your own types.
6 Likes
Well, this something I would like to offer the users of my package. Broadcasting is nice but so would be !functions but I’m not being able to implement it because I can’t see how to access the elements of A
& B
in above example.
I’ll look at the broadcasting too.
BTW, I’m curious about the +=
operator. Where is it defined?
It’s not a generic operator. a += b
is lowered to a = a + b
, there’s no way to overload that. If you use the broadcasting machinery like @stevengj suggested, you could intercept a .= ...
and therefore also a .+= ...
though, through Base.materialize!
.
2 Likes
The broadcast machinery looks quite complex, and to start with the type in question does not have an axes
as mentioned necessary
julia> G1 = mat2grid([0.0 1; 2 3]);
julia> axes(G1)
ERROR: MethodError: no method matching size(::GMT.GMTgrid)
Perhaps I’m missing something, but your type looks to me like a special matrix type, so is there a reason why you wouldn’t want to define size
on it and implement the AbstractArray
interface for it?
1 Like
No, nothing against, only ignorance. But again implementing that interface is not obvious. The Squares
example in the docs is too simplistic. Would I have to declare only the type as <: AbstractArray
or do I have to do it also for the array members? But I want some of them to remain Float64
only.
(and thanks for the advices)
Yes, your type should subtype AbstractArray{T,N}
, where T
is the element type and N
the number of dimensions. It doesn’t matter what types the members of your custom array type are, that’s not important for dispatch. You probably want to implement at least Base.size
and Base.getindex
, which should already get you on your way. You can then try doing things like broadcasting, usually you should be able to see from the stacktraces, which additional functions you still need to implement to support a certain feature, but I think there is something in the manual about that as well.
2 Likes
OK, it was not that easy but I finally managed to implement broadcasting. I have more questions but will leave it to another post. I found, however, a strange behavior that even looks like a bug. The implementation is here
This works like expected
julia> G1 = mat2grid([0.0 pi/4; pi pi/2])
2Ă—2 GMT.GMTgrid{Float64,2}:
0.0 0.785398
3.14159 1.5708
julia> G2 = cos.(G1)
2Ă—2 GMT.GMTgrid{Float64,2}:
1.0 0.707107
-1.0 6.12323e-17
julia> acos.(G2)
2Ă—2 GMT.GMTgrid{Float64,2}:
0.0 0.785398
3.14159 1.5708
julia> cos(G1)
ERROR: MethodError: no method matching exp!(::GMT.GMTgrid{Complex{Float64},2})
Closest candidates are:
exp!(::StridedArray{T, 2}) where T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64} at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\LinearAlgebra\src\dense.jl:554
Stacktrace:
[1] cos(::GMT.GMTgrid{Float64,2}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\LinearAlgebra\src\dense.jl:808
[2] top-level scope at REPL[6]:1
but this, which takes quite some time to compute at first time, does not error like the above last case and gives the following result. This is very dangerous because it results from a mistake very easy to make.
julia> acos(G2)
2Ă—2 Array{Complex{Float64},2}:
0.651095+5.55112e-17im -0.71526+5.55112e-17im
1.01153+0.0im 1.66263-2.22045e-16im
It’s giving you the correct answer — the acos
of a square matrix is perfectly well defined and that is what it is giving you.
3 Likes
It’s not the correct answer. The correct is given by acos.()
. See, not even the type is correct. And it’s not only acos
julia> G1 = mat2grid([0.0 pi/4; pi pi/2])
2Ă—2 GMT.GMTgrid{Float64,2}:
0.0 0.785398
3.14159 1.5708
julia> G2 = cos.(G1)
2Ă—2 GMT.GMTgrid{Float64,2}:
1.0 0.707107
-1.0 6.12323e-17
julia> acos.(G2)
2Ă—2 GMT.GMTgrid{Float64,2}:
0.0 0.785398
3.14159 1.5708
julia> acos(G2)
2Ă—2 Array{Complex{Float64},2}:
0.651095+5.55112e-17im -0.71526+5.55112e-17im
1.01153+0.0im 1.66263-2.22045e-16im
julia> using Statistics
julia> mean.(G1)
2Ă—2 GMT.GMTgrid{Float64,2}:
0.0 0.785398
3.14159 1.5708
julia> mean(G1)
1.3744467859455345
julia> std(G1)
1.3413227186681242
Steven is referring to matrix functions. Read ?acos(rand(2,2))
.
4 Likes