{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Julia pass-by-sharing : cautions and benefits  \n",
    "Jeff Fessler  \n",
    "2019-02-06"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Julia uses a convention called \"pass-by-sharing\"\n",
    "for passing arguments to functions\n",
    "[https://docs.julialang.org/en/latest/manual/functions/#Argument-Passing-Behavior-1]\n",
    "\n",
    "Here is an illustration / cautionary note."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "f2 (generic function with 1 method)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function f1(x)\n",
    "    x = x .+ 1\n",
    "    return x\n",
    "end\n",
    "\n",
    "function f2(x)\n",
    "    x[1] += 1\n",
    "#   x[:] = x[:] .+ 1\n",
    "#   push!(x, 5)\n",
    "    return x\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(a, b) = ([3, 3], [4, 4])\n",
      "(c, d) = ([4, 3], [4, 3])\n"
     ]
    }
   ],
   "source": [
    "a = [3,3]\n",
    "b = f1(a)\n",
    "@show a,b\n",
    "\n",
    "c = [3,3]\n",
    "d = f2(c)\n",
    "@show c,d\n",
    ";"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same thing happens with variables, even without functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(a, b, b === a) = ([3, 3], [4, 4], false)\n",
      "(c, d, c === d) = ([3, 3], [4, 3], false)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "([3, 3], [4, 3], false)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [3,3]\n",
    "b = a\n",
    "b = b .+ 1\n",
    "@show a,b, b===a\n",
    "\n",
    "c = [3,3]\n",
    "d = copy(c)\n",
    "d[1] += 1\n",
    "@show c,d, c===d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The reason is that `d = c` does not copy `c`\n",
    "but just makes \"another pointer\" to the same data,\n",
    "and then\n",
    "`d[1] = 5` is \"syntatic sugar\" for\n",
    "`setindex!(d, 1, 5)`\n",
    "and the `!` there reminds you that `d` will be modified,\n",
    "but `d` points to the same data as `c`\n",
    "so `c` is also modified here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Why call-by-sharing is useful"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "gd! (generic function with 1 method)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function gd!(x, g::Function, L::Number, niter::Number)\n",
    "    for iter=1:niter\n",
    "            x .+= g(x)/L # in-place update - for large-scale problems\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Surprise: `+=` is not in place but `.+=` is"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "g2 (generic function with 1 method)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function g1(x, y)\n",
    "    x += y # this does not mutate input x\n",
    "    return x\n",
    "end\n",
    "\n",
    "function g2(x, y)\n",
    "    x .+= y # this does modify input x !\n",
    "    return x\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(a, b) = ([3, 3], [4, 4])\n",
      "(c, d) = ([4, 4], [4, 4])\n"
     ]
    }
   ],
   "source": [
    "a = [3,3]\n",
    "u = [1,1]\n",
    "b = g1(a,u)\n",
    "@show a,b\n",
    "\n",
    "c = [3,3]\n",
    "d = g2(c,u)\n",
    "@show c,d\n",
    ";"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Bottom line: to be safe, avoid using function arguments on LHS   unless you want it to be mutating, like `gd!` above, in which case use `!` in function name."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 1.1.0",
   "language": "julia",
   "name": "julia-1.1"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.1.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
