Skip to content

Commit 2c3685f

Browse files
authored
Dev (#339)
Temporary hard-coded GeneralizedGenerated fork
1 parent 9b057c0 commit 2c3685f

30 files changed

+2637
-9
lines changed

Project.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
name = "Soss"
22
uuid = "8ce77f84-9b61-11e8-39ff-d17a774bf41c"
33
author = ["Chad Scherrer <[email protected]>"]
4-
version = "0.21.0"
4+
version = "0.21.1"
55

66
[deps]
77
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
8+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
89
DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
910
DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
1011
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
1112
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
12-
GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb"
1313
IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
1414
JuliaVariables = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec"
1515
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -27,6 +27,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
2727
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
2828
RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47"
2929
SampleChains = "754583d1-7fc4-4dab-93b5-5eaca5c9622e"
30+
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
3031
SimpleGraphs = "55797a34-41de-5266-9ec1-32ac4eb504d3"
3132
SimplePartitions = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d"
3233
SimplePosets = "b2aef97b-4721-5af9-b440-0bad754dc5ba"
@@ -41,11 +42,11 @@ TupleVectors = "615932cf-77b6-4358-adcd-5b7eba981d7e"
4142

4243
[compat]
4344
ArrayInterface = "5,6"
45+
DataStructures = "0.18"
4446
DensityInterface = "0.4"
4547
DiffResults = "1"
4648
Distributions = "0.23, 0.24, 0.25"
4749
FillArrays = "0.11, 0.12, 0.13"
48-
GeneralizedGenerated = "0.3"
4950
IfElse = "0.1"
5051
JuliaVariables = "0.2"
5152
MLStyle = "0.3,0.4"

src/GG/.travis.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Documentation: http://docs.travis-ci.com/user/languages/julia/
2+
language: julia
3+
os:
4+
- linux
5+
- osx
6+
julia:
7+
- 1.0
8+
- 1.1
9+
- 1.2
10+
- nightly
11+
matrix:
12+
allow_failures:
13+
- julia: nightly
14+
fast_finish: true
15+
notifications:
16+
email: false
17+
after_success:
18+
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())'
19+
jobs:
20+
include:
21+
- stage: Documentation
22+
julia: 1.0
23+
script: julia --project=docs -e '
24+
using Pkg;
25+
Pkg.develop(PackageSpec(path=pwd()));
26+
Pkg.instantiate();
27+
include("docs/make.jl");'
28+
after_success: skip

src/GG/LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
Copyright (c) 2019 thautwarm
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20+
OR OTHER DEALINGS IN THE SOFTWARE.

src/GG/Project.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name = "GeneralizedGenerated"
2+
uuid = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb"
3+
authors = ["thautwarm"]
4+
version = "0.3.3"
5+
6+
[deps]
7+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
8+
JuliaVariables = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec"
9+
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
10+
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
11+
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
12+
13+
[compat]
14+
DataStructures = "0.17, 0.18"
15+
JuliaVariables = "~0.2.4"
16+
MLStyle = "~0.4.2"
17+
MacroTools = "0.5"
18+
julia = "1"
19+
20+
[extras]
21+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
22+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
23+
24+
[targets]
25+
test = ["Test", "BenchmarkTools"]

src/GG/README.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# GeneralizedGenerated
2+
3+
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaStaging.github.io/GeneralizedGenerated.jl/stable)
4+
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaStaging.github.io/GeneralizedGenerated.jl/dev)
5+
[![Build Status](https://travis-ci.com/JuliaStaging/GeneralizedGenerated.jl.svg?branch=master)](https://travis-ci.com/JuliaStaging/GeneralizedGenerated.jl)
6+
[![Codecov](https://codecov.io/gh/JuliaStaging/GeneralizedGenerated.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaStaging/GeneralizedGenerated.jl)
7+
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3596233.svg)](https://doi.org/10.5281/zenodo.3596233)
8+
9+
GeneralizedGenerated enables the generalized generated functions. Specifically, **it supports closure constructions in generated functions**.
10+
11+
Besides, some utility stuffs relevant to GeneralizedGenerated's implementation are exported,
12+
which **allows you to keep `eval` and `invokelastest`** away from Julia
13+
metaprogramming.
14+
15+
## Notes about Usage:
16+
17+
`GeneralizedGenerated.jl` has issues about latency and extensive memory consumptions, and is sometimes likely to trigger segfault bugs when generated functions get enormous([#45](https://github.com/JuliaStaging/GeneralizedGenerated.jl/issues/45), [#59](https://github.com/JuliaStaging/GeneralizedGenerated.jl/issues/59)). This suggests that you should avoid your expressions from being too large.
18+
19+
In terms of **use cases where no closure is needed**, you'd better use [RuntimeGeneratedFunctions.jl](https://github.com/SciML/RuntimeGeneratedFunctions.jl), which has better scalability than `GeneralizedGenerated.jl`.
20+
21+
P.S:
22+
- You should also re-check if closures are really necessary in your code.
23+
- If you use `mk_function` or similar stuffs in a non-global loop, but only call those generated functions once, you might re-think if your design can be refined to avoid this.
24+
25+
## Background: World Age Problem
26+
27+
See an explanation [here](https://discourse.julialang.org/t/world-age-problem-explanation/9714/4).
28+
29+
```julia
30+
julia> module WorldAgeProblemRaisedHere!
31+
do_this!(one_ary_fn_ast::Expr, arg) = begin
32+
eval(one_ary_fn_ast)(arg)
33+
end
34+
res = do_this!(:(x -> x + 1), 2)
35+
@info res
36+
end
37+
ERROR: MethodError: no method matching (::getfield(Main.WorldAgeProblemRaisedHere!, Symbol("##1#2")))(::Int64)
38+
The applicable method may be too new: running in world age 26095, while current world is 26096.
39+
40+
julia> module WorldAgeProblemSolvedHere!
41+
42+
do_this!(one_ary_fn_ast::Expr, arg) = begin
43+
runtime_eval(one_ary_fn_ast)(arg)
44+
end
45+
res = do_this!(:(x -> x + 1), 2)
46+
@info res
47+
end
48+
[ Info: 3
49+
Main.WorldAgeProblemSolvedHere!
50+
```
51+
52+
## Support Closures in Generated Functions
53+
54+
```julia
55+
56+
57+
@gg function f(x)
58+
quote
59+
a -> x + a
60+
end
61+
end
62+
63+
f(1)(2) # => 3
64+
65+
@gg function h(x, c)
66+
quote
67+
d = x + 10
68+
function g(x, y=c)
69+
x + y + d
70+
end
71+
end
72+
end
73+
74+
h(1, 2)(1) # => 14
75+
```
76+
77+
Note there're some restrictions to the generalized generated functions yet:
78+
79+
- Multiple dispatch is not allowed, and `f(x) = ...` is equivalent to `f = x -> ...`. This will never gets supported for it needs a thorough implementation of multiple dispatch in GG.
80+
- Comprehensions for generated functions are not implemented yet. It won't cost a long time for being supported.
81+
82+
The evaluation module can be specified in this way:
83+
84+
```julia
85+
julia> module S
86+
run(y) = y + 1
87+
end
88+
Main.S
89+
90+
julia> @gg g(m::Module, y) = @under_global :m :(run(y));
91+
# the global variable `run` is from the local variable `m`
92+
# <=>
93+
# @gg g(m::Module, y) = :($(:m).run(y));
94+
95+
julia> g(S, 1)
96+
2
97+
```
98+
99+
Of course you can use structures to imitate modules:
100+
101+
```julia
102+
julia> struct S
103+
run :: Function
104+
end
105+
Main.S
106+
107+
julia> @gg function g(m::S, y)
108+
@under_global :m quote
109+
run(y)
110+
end
111+
end;
112+
# <=>
113+
# @gg function g(m::S, y)
114+
# :($(:m).run(y))
115+
# end;
116+
117+
julia> g(S(x -> x + 1), 1)
118+
2
119+
120+
julia> const pseudo_module = S(x -> x + 1);
121+
julia> @gg function g(y)
122+
@under_global pseudo_module quote
123+
run(y)
124+
end
125+
end
126+
# <=>
127+
# @gg function g(y)
128+
# :($(pseudo_module).run(y))
129+
# end
130+
julia> g(1)
131+
2
132+
```
133+
134+
julia> @generated function g()
135+
Module = Main
136+
mk_expr(Module, :( (x -> x)(1)))
137+
end
138+
139+
## No `eval`/`invokelatest`!
140+
141+
```julia
142+
# do something almost equivalent to `eval`
143+
# without introducing the world age problem!
144+
145+
f = mk_function(:((x, y) -> x + y))
146+
f(1, 2)
147+
# => 3
148+
149+
f = mk_function([:x, :y]#= args =#, []#= kwargs =#, :(x + y))
150+
f(1, 2)
151+
# => 3
152+
153+
154+
module GoodGame
155+
xxx = 10
156+
end
157+
# Specify global module
158+
f = mk_function(GoodGame, :(function () xxx end))
159+
f()
160+
# => 10
161+
```
162+
163+
The function created by `mk_function` always has the signature `f(args…; kwargs…) = ...` if you need to use the function in a context where it will be passed multiple arguments, use the following pattern
164+
165+
```julia
166+
f = mk_function(:((x, y) -> x + y))
167+
168+
function F(g, pairs)
169+
map(pairs) do (x,y)
170+
g(x,y)
171+
end
172+
end
173+
174+
pairs = zip(1:10,2:11)
175+
F((x,y)->f(x,y), pairs)
176+
#=
177+
=>
178+
10-element Array{Int64,1}:
179+
3
180+
5
181+
7
182+
9
183+
11
184+
13
185+
15
186+
17
187+
19
188+
21
189+
=#
190+
```
191+
192+
Tips
193+
==============
194+
195+
Note, `mk_function` just accepts a function-like AST, to eval more kinds of
196+
ASTs, use `runtime_eval`:
197+
198+
```julia
199+
a = 0
200+
runtime_eval(:(a + 1)) == 1 # true
201+
202+
module GoodGameOnceAgain
203+
a = 2
204+
end
205+
runtime_eval(GoodGameOnceAgain, :(a + 3)) == 5
206+
```
207+
208+
# Known Bugs
209+
210+
1. Type annotations.
211+
212+
Type annotations for cell variables (variables shared to any inner functions of the current scope) do not work. You might consider changing your generated code from
213+
214+
```julia
215+
a :: t = b
216+
# when 'a' is cell,
217+
# the closure-converted code 'a.contents :: t = b' fails due to the Julia syntax
218+
```
219+
220+
to
221+
222+
```julia
223+
a = b :: t
224+
```
225+
226+
2. Precompilation
227+
228+
GG is designed for purely runtime generated functions, and currently has difficulties in precompiling a GG function.
229+
230+
When developing a package, please do not define a GG function in the top level!

0 commit comments

Comments
 (0)