Created
January 27, 2019 19:37
-
-
Save nlw0/bd698d2785d3cf1d98e2ac1e6e2a7d31 to your computer and use it in GitHub Desktop.
Julia macro playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Julia macro playground\n", | |
"============\n", | |
"\n", | |
"We first start by defining some silly basic functions that would be later called in sequence in our \"application\"." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 20, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"process_2 (generic function with 1 method)" | |
] | |
}, | |
"execution_count": 20, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"function open_db(source)\n", | |
" return 100, 10\n", | |
"end\n", | |
"\n", | |
"function save_data(target, data)\n", | |
" return true\n", | |
"end\n", | |
"\n", | |
"function get_data(source)\n", | |
" aa, bb = open_db(source)\n", | |
" return rand(aa, bb)\n", | |
"end\n", | |
"\n", | |
"function process_1(data)\n", | |
" data[:,1:5] + data[:,6:10]\n", | |
"end\n", | |
"\n", | |
"function process_2(data)\n", | |
" sum(data, dims=1)\n", | |
"end" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This is the \"application\". Although it is doing some stuff, we can't see much." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"true" | |
] | |
}, | |
"execution_count": 21, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"function my_process()\n", | |
" dd = get_data(\"mydb1\")\n", | |
" xx = process_1(dd)\n", | |
" yy = process_2(xx)\n", | |
" save_data(\"mydb2\", yy)\n", | |
"end\n", | |
"\n", | |
"my_process()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now we create a macro that goes through the function definition and inserts print statements everywhere. Like a \"verbose version\" of the original function.\n", | |
"\n", | |
"Notice that we will get the logging behavior even though the function we wrote is just the same, and only has our \"business logic\" nothing about printing anywhere." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"[start] dd = get_data(\"mydb1\")\n", | |
"[stop] dd = get_data(\"mydb1\")\n", | |
"[start] xx = process_1(dd)\n", | |
"[stop] xx = process_1(dd)\n", | |
"[start] yy = process_2(xx)\n", | |
"[stop] yy = process_2(xx)\n", | |
"[start] save_data(\"mydb2\", yy)\n", | |
"[stop] save_data(\"mydb2\", yy)\n" | |
] | |
} | |
], | |
"source": [ | |
"macro logger(func)\n", | |
" chatty_exprs = [\n", | |
" if (typeof(ex) == LineNumberNode)\n", | |
" [ex]\n", | |
" else\n", | |
" [:(println(\"[start] \", $(string(ex)))),\n", | |
" ex,\n", | |
" :(println(\"[stop] \", $(string(ex))))]\n", | |
" end\n", | |
" for ex in func.args[2].args]\n", | |
" func.args[2].args = collect(Iterators.flatten(chatty_exprs))\n", | |
" return func\n", | |
"end\n", | |
"\n", | |
"@logger function my_process_logged()\n", | |
" dd = get_data(\"mydb1\")\n", | |
" xx = process_1(dd)\n", | |
" yy = process_2(xx)\n", | |
" save_data(\"mydb2\", yy)\n", | |
"end\n", | |
"\n", | |
"my_process_logged()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"In this example we are again doing something that might be useful for some sort of debugging. We don't touch the original code, but we insert a function call in the middle of the logic that runs some test in the intermediate data." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 24, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Check success!\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"true" | |
] | |
}, | |
"execution_count": 24, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"function check_data(xx)\n", | |
" if size(xx, 1) > 10\n", | |
" println(\"Check success!\")\n", | |
" else\n", | |
" println(\"Check failed...\")\n", | |
" end\n", | |
" return nothing\n", | |
"end\n", | |
"\n", | |
"macro do_check(func)\n", | |
" chatty_exprs = [\n", | |
" if typeof(ex) == Expr && typeof(ex.args[2]) == Expr && length(ex.args) > 1 && ex.args[2].args[1] == :process_1\n", | |
" [ex, :(check_data($(ex.args[1])))]\n", | |
" else\n", | |
" [ex]\n", | |
" end\n", | |
" for ex in func.args[2].args]\n", | |
" func.args[2].args = collect(Iterators.flatten(chatty_exprs))\n", | |
" return func\n", | |
"end\n", | |
"\n", | |
"@do_check function my_process_chk()\n", | |
" dd = get_data(\"mydb1\")\n", | |
" xx = process_1(dd)\n", | |
" yy = process_2(xx)\n", | |
" save_data(\"mydb2\", yy)\n", | |
"end\n", | |
"\n", | |
"my_process_chk()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The `@macroexpand` macrro shows what the generated code looks like." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 26, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
":(function (Main.my_process_chk)()\n", | |
" #= In[26]:2 =#\n", | |
" #66#dd = (Main.get_data)(\"mydb1\")\n", | |
" #= In[26]:3 =#\n", | |
" #67#xx = (Main.process_1)(#66#dd)\n", | |
" (Main.check_data)(#67#xx)\n", | |
" #= In[26]:4 =#\n", | |
" #68#yy = (Main.process_2)(#67#xx)\n", | |
" #= In[26]:5 =#\n", | |
" (Main.save_data)(\"mydb2\", #68#yy)\n", | |
" end)" | |
] | |
}, | |
"execution_count": 26, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"@macroexpand @do_check function my_process_chk()\n", | |
" dd = get_data(\"mydb1\")\n", | |
" xx = process_1(dd)\n", | |
" yy = process_2(xx)\n", | |
" save_data(\"mydb2\", yy)\n", | |
"end" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Julia 1.0.3", | |
"language": "julia", | |
"name": "julia-1.0" | |
}, | |
"language_info": { | |
"file_extension": ".jl", | |
"mimetype": "application/julia", | |
"name": "julia", | |
"version": "1.0.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment