-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
functor.jl
94 lines (76 loc) · 2.37 KB
/
functor.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function functor end
const NoChildren = Tuple{}
"""
@leaf T
Define [`functor`](@ref) for the type `T` so that `isleaf(x::T) == true`.
"""
macro leaf(T)
:($Functors.functor(::Type{<:$(esc(T))}, x) = ($Functors.NoChildren(), _ -> x))
end
@leaf Any # every type is a leaf by default
functor(x) = functor(typeof(x), x)
functor(::Type{<:Tuple}, x) = x, identity
functor(::Type{<:NamedTuple{L}}, x) where L = NamedTuple{L}(map(s -> getproperty(x, s), L)), identity
functor(::Type{<:Dict}, x) = Dict(k => x[k] for k in keys(x)), identity
functor(::Type{<:AbstractArray}, x) = x, identity
@leaf AbstractArray{<:Number}
function makefunctor(m::Module, T, fs = fieldnames(T))
fidx = Ref(0)
escargs = map(fieldnames(T)) do f
f in fs ? :(y[$(fidx[] += 1)]) : :(x.$f)
end
escargs_nt = map(fieldnames(T)) do f
f in fs ? :(y[$(Meta.quot(f))]) : :(x.$f)
end
escfs = [:($f=x.$f) for f in fs]
@eval m begin
function $Functors.functor(::Type{<:$T}, x)
reconstruct(y) = $T($(escargs...))
reconstruct(y::NamedTuple) = $T($(escargs_nt...))
return (;$(escfs...)), reconstruct
end
end
end
function functorm(T, fs = nothing)
fs === nothing || Meta.isexpr(fs, :tuple) || error("@functor T (a, b)")
fs = fs === nothing ? [] : [:($(map(QuoteNode, fs.args)...),)]
:(makefunctor(@__MODULE__, $(esc(T)), $(fs...)))
end
macro functor(args...)
functorm(args...)
end
isleaf(@nospecialize(x)) = children(x) === NoChildren()
children(x) = functor(x)[1]
###
### FlexibleFunctors.jl
###
function makeflexiblefunctor(m::Module, T, pfield)
pfield = QuoteNode(pfield)
@eval m begin
function $Functors.functor(::Type{<:$T}, x)
pfields = getproperty(x, $pfield)
function re(y)
all_args = map(fn -> getproperty(fn in pfields ? y : x, fn), fieldnames($T))
return $T(all_args...)
end
func = NamedTuple{pfields}(map(p -> getproperty(x, p), pfields))
return func, re
end
end
end
function flexiblefunctorm(T, pfield = :params)
pfield isa Symbol || error("@flexiblefunctor T param_field")
pfield = QuoteNode(pfield)
:(makeflexiblefunctor(@__MODULE__, $(esc(T)), $(esc(pfield))))
end
macro flexiblefunctor(args...)
flexiblefunctorm(args...)
end
###
### Compat
###
if VERSION < v"1.7"
# Function in 1.7 checks t.name.flags & 0x2 == 0x2,
# but for 1.6 this seems to work instead:
ismutabletype(@nospecialize t) = t.mutable
end