Skip to content

Instantly share code, notes, and snippets.

@nlw0
Created January 27, 2019 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nlw0/bd698d2785d3cf1d98e2ac1e6e2a7d31 to your computer and use it in GitHub Desktop.
Save nlw0/bd698d2785d3cf1d98e2ac1e6e2a7d31 to your computer and use it in GitHub Desktop.
Julia macro playground
Display the source blob
Display the rendered blob
Raw
{
"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