# Source code of a function

**URL:** <https://discourse.julialang.org/t/source-code-of-a-function/4499>\
**Category:** General Usage\
**Created:** [June 27, 2017, 5:37pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499 "2017-06-27T17:37:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![rcnlee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rcnlee/32/4107_2.png) [@rcnlee](https://discourse.julialang.org/u/rcnlee)\
**Post date:** [June 27, 2017, 5:37pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/1 "2017-06-27T17:37:20Z")

</div>

Hello,

Is it possible to recover the source code of a function as a string?  
For example,

```julia
f(x,y) = 2sin(x+y)+1

```

Is there a function call on `f` that can get me `s="2sin(x+y)+1"`?

Thanks!

---

<div class="post-metadata">

**Author:** ![ExpandingMan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/expandingman/32/866_2.png) [@ExpandingMan](https://discourse.julialang.org/u/ExpandingMan)\
**Post date:** [June 27, 2017, 5:49pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/2 "2017-06-27T17:49:02Z")

</div>

If you do

```julia
@which f(x,y)

```

it will print the file and line number where the function was defined (or it will tell you it was done in the REPL). Try, for example `@which sin(π)`.

---

<div class="post-metadata">

**Author:** ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)\
**Post date:** [June 27, 2017, 6:08pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/3 "2017-06-27T18:08:23Z")

</div>

In the REPL you could also try

```julia
@less f(x,y)

```

which wraps the call to the pager on the result from `@which`

---

<div class="post-metadata">

**Author:** ![ihnorton](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihnorton/32/26_2.png) [@ihnorton](https://discourse.julialang.org/u/ihnorton)\
**Post date:** [June 27, 2017, 7:10pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/4 "2017-06-27T19:10:37Z")

</div>

For reference, the source expression is available using the following:

`Base.uncompressed_ast(methods(f).ms[1]).code`

But right now it is necessary to go to the original code to get the unparsed version, as suggested above. It’s on the roadmap for 1.0; see [this issue](https://github.com/JuliaLang/julia/issues/21774) and others cross-referenced there.

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [June 27, 2017, 11:03pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/5 "2017-06-27T23:03:31Z")

</div>

Take a look at Sugar.jl, specifically to its [`macro_form()`](https://github.com/SimonDanisch/Sugar.jl/blob/a62d6b10b583ae9437322dd921902f127c4cdc10/src/lambdas.jl#L213-L232) function. It first tries to find and parse function’s source, and only if it’s unavailable, falls back to parsing AST. `macro_form` often fails for functions generated using `@eval` (since source code includes interpolated values from the context) and may produce somewhat different results for functions defined in REPL, but in general I found this function extremely helpful.

---

<div class="post-metadata">

**Author:** ![rcnlee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rcnlee/32/4107_2.png) [@rcnlee](https://discourse.julialang.org/u/rcnlee)\
**Post date:** [June 29, 2017, 3:08am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/6 "2017-06-29T03:08:01Z")

</div>

Thanks! That’s exactly what I was looking for.

In particular, these lines did it:

```julia
using Sugar
method = Sugar.get_method(f, types)
code, str = Sugar.get_source(method)

```

---

<div class="post-metadata">

**Author:** ![LaurentPlagne](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/laurentplagne/32/10103_2.png) [@LaurentPlagne](https://discourse.julialang.org/u/LaurentPlagne)\
**Post date:** [May 4, 2018, 10:46pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/7 "2018-05-04T22:46:16Z")

</div>

> [@rcnlee](#):
>
> method = Sugar.get\_method

It took me some time to realize that `types` is supposed to be a tuple.  
So, if you want to get the source of a method with the type Vector{Float64} you need to write :  
`method = Sugar.get_method(f, (Vector{Float64),))`

---

<div class="post-metadata">

**Author:** ![iwelch](https://avatars.discourse-cdn.com/v4/letter/i/8c91f0/32.png) [@iwelch](https://discourse.julialang.org/u/iwelch)\
**Post date:** [May 5, 2018, 4:30pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/8 "2018-05-05T16:30:27Z")

</div>

> [@ihnorton](#):
>
> But right now it is necessary to go to the original code to get the unparsed version, as suggested above. It’s on the roadmap for 1.0; see this issue and others cross-referenced there.

well, the Gallium debugger seems to be capable of showing the source, either in a file or if entered from the REPL. how does it do it?

and, more importantly, in 1.0, what is the syntax that I should use to view the method source for function f?

regards,

/iaw

---

<div class="post-metadata">

**Author:** ![LaurentPlagne](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/laurentplagne/32/10103_2.png) [@LaurentPlagne](https://discourse.julialang.org/u/LaurentPlagne)\
**Post date:** [May 5, 2018, 5:40pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/9 "2018-05-05T17:40:15Z")

</div>

The rcnlee procedure with Sugar.jl works just fine…

---

<div class="post-metadata">

**Author:** ![ihnorton](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihnorton/32/26_2.png) [@ihnorton](https://discourse.julialang.org/u/ihnorton)\
**Post date:** [May 7, 2018, 1:16pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/10 "2018-05-07T13:16:57Z")

</div>

IIRC Gallium reparses the source.

---

<div class="post-metadata">

**Author:** ![bsnyh](https://avatars.discourse-cdn.com/v4/letter/b/ce7236/32.png) [@bsnyh](https://discourse.julialang.org/u/bsnyh)\
**Post date:** [April 2, 2019, 9:17am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/11 "2019-04-02T09:17:50Z")

</div>

I’m using Julia 1.0.2, it looks like I can not install Sugar package now.

> |\_\_/ |
> 
> julia\> using Sugar  
> ERROR: ArgumentError: Package Sugar not found in current path:
> 
> - Run `import Pkg; Pkg.add("Sugar")` to install the Sugar package.
> 
> Stacktrace:  
> [1] require(::Module, ::Symbol) at ./loading.jl:823
> 
> julia\> import Pkg
> 
> julia\> Pkg.add(“Sugar”)  
> Updating registry at `~/.julia/registries/General`  
> Updating git-repo `https://github.com/JuliaRegistries/General.git`  
> Resolving package versions…  
> ERROR: Unsatisfiable requirements detected for package Matcha [8d673c98]:  
> Matcha [8d673c98] log:  
> ├─possible versions are: [0.0.1-0.0.2, 0.1.0-0.1.1] or uninstalled  
> ├─restricted by compatibility requirements with Sugar [e18849f4] to versions: [0.0.1-0.0.2, 0.1.0-0.1.1]  
> │ └─Sugar [e18849f4] log:  
> │ ├─possible versions are: [0.0.1-0.0.3, 0.1.0, 0.2.0, 0.3.0-0.3.1, 0.4.0-0.4.5] or uninstalled  
> │ └─restricted to versions \* by an explicit requirement, leaving only versions [0.0.1-0.0.3, 0.1.0, 0.2.0, 0.3.0-0.3.1, 0.4.0-0.4.5]  
> └─restricted by julia compatibility requirements to versions: uninstalled — no versions left

---

<div class="post-metadata">

**Author:** ![pfitzseb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pfitzseb/32/45566_2.png) [@pfitzseb](https://discourse.julialang.org/u/pfitzseb)\
**Post date:** [April 2, 2019, 10:27am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/12 "2019-04-02T10:27:04Z")

</div>

Take a look at [CodeTracking.jl](https://github.com/timholy/CodeTracking.jl).

---

<div class="post-metadata">

**Author:** ![ignace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ignace/32/24275_2.png) [@ignace](https://discourse.julialang.org/u/ignace)\
**Post date:** [April 18, 2021, 8:51pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/13 "2021-04-18T20:51:40Z")

</div>

Hello! Is Sugar.jl still supported nowadays?

---

<div class="post-metadata">

**Author:** ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)\
**Post date:** [May 23, 2021, 12:10am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/14 "2021-05-23T00:10:09Z")

</div>

If we’re talking about this [Sugar.jl](https://github.com/SimonDanisch/Sugar.jl), it was updated 3 months ago.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [February 28, 2022, 10:15pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/15 "2022-02-28T22:15:08Z")

</div>

I know this is an old thread, but I was just looking for any discussion of this. Yes, it was updated in 2021, but the last release is from 2018, and the README now fails:

```julia
julia> Sugar.sugared(controlflow_1, (Int, Int), code_lowered)
ERROR: type Expr has no field typ
Stacktrace:
  [1] getproperty
    @ ./Base.jl:38 [inlined]
  [2] similar_expr(x::Expr, args::Vector{Vector{Any}})
    @ Sugar ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:6
  [3] _normalize_ast(expr::Expr)
    @ Sugar ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:108
  [4] replace_or_drop(f::typeof(Sugar._normalize_ast), drop::Sugar.var"#5#6", ast::Expr, result::Vector{Any})
    @ Sugar ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:32
  [5] replace_or_drop(f::Function, drop::Function, ast::Vector{Any}, result::Vector{Any})
    @ Sugar ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:25
  [6] replace_or_drop
    @ ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:24 [inlined]
  [7] replace_expr
    @ ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:21 [inlined]
  [8] normalize_ast
    @ ~/.julia/packages/Sugar/nhFic/src/ast_tools.jl:113 [inlined]
  [9] sugared(f::Function, types::Tuple{DataType, DataType}, stage::Function)
    @ Sugar ~/.julia/packages/Sugar/nhFic/src/sugarcoating.jl:92
 [10] top-level scope
    @ REPL[110]:1

```

Recovering an Expr from CodeInfo can be really useful, makes me wonder what it would take to get it working again…

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [March 1, 2022, 11:17am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/16 "2022-03-01T11:17:12Z")

</div>

As far as I know, there’s no 1-to-1 mapping between `Expr` and `CodeInfo`. But you can try to:

1. Generate new `Expr` from `CodeInfo`. Simple function calls should be pretty similar to the original code, but complex expressions, control flow, macros and other high-level constructs will be lost.
2. Find the path to the source code from lineinfo embedded in `CodeInfo`. However, this has never been reliably for me, even with `Sugar`.

If you describe your use case, I may have additional tips on one or both of these approaches.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [March 1, 2022, 2:39pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/17 "2022-03-01T14:39:56Z")

</div>

The use case is probabilistic programming. In the current release of Soss.jl, a `Model` is a block of `Statement`s, each of which is either an `Assign` (`lhs = rhs`) or a `Sample` (`lhs ~ rhs`). I want to make this more flexible and allow control flow, especially for things like more complex dependencies among entries of an array.

I have a good start on this, mostly using ideas in [this post](https://discourse.julialang.org/t/new-soss-interface-request-for-feedback/65118). The current challenge is the question of how to update a sampled value and make sure to maintain consistency of the dependency graph.

It’s very popular to do this using something like a nested dictionary data structure to represent the trace. But this has a lot of overhead, and I’d much rather have something with smaller scope of use cases than something that’s slow. I had been thinking the “trace” could just be made of local variables, so it’s all in code with no data structure at all. At each `~`, we could add a `@label` and a `@goto`, and the `tilde` function could pass back information about where to jump next.

Then I started poking around some more in @BenLauwens 's ResumableFunctions.jl. I had played with this before, but he’s now gotten it to be really fast. There are some cool ideas in the implementation:  
[https://benlauwens.github.io/ResumableFunctions.jl/dev/internals/](https://benlauwens.github.io/ResumableFunctions.jl/dev/internals/)

The mutable struct + state machine approach is really slick, and it’s really clever to use the typed code to get the types for the slots. But for my purposes, the name shadowing could get in the way. Reusing a variable name leads to one entry in the struct taking on multiple roles. This could lead to problems when we start jumping around.

Luckily, I think @thautwarm 's JuliaVariables.jl can be made to help with this. I opened a new issue about that here:  
[https://github.com/JuliaStaging/JuliaVariables.jl/issues/28](https://github.com/JuliaStaging/JuliaVariables.jl/issues/28)

So I think the process would look something like

1. Use JuliaVariables to solve for scopes and make sure names are unique
2. Still at the AST level, make transformations like those described [here](https://juliadebug.github.io/JuliaInterpreter.jl/stable/ast/) to get us closer to the lowered representation
3. Add `@label`s and `@goto`s to make it easy to jump between samples

At this point, we probably need to replace each `~` with a concrete `tilde` function, depending whether we want to call `rand` or `logdensityof`, or something else. Then

1. Lower the code
2. Use LoweredCodeUtils.jl to get the edges ([Edges · LoweredCodeUtils](https://juliadebug.github.io/LoweredCodeUtils.jl/stable/edges/))

Then, for any variable that’s updated, the edges tell us what else needs to be done. So maybe it’s making an abbreviated version of the code for each of these cases, or adding some jumps to cover each of these cases. Not really sure yet.

Then I don’t know the best way to “run” lowered code. It’s important to avoid world age issues, obviously. I’ve been very happy with GeneralizedGenerated, but if this lowered code is lifted back up to the AST level it will be much bigger and (IIUC) any closures will be gone. So maybe it would need RuntimeGeneratedFunctions.jl? Or maybe lowered code can be executed directly? I’m not sure yet about some of these things.

Ok, this response got pretty long. Maybe it’s a little too down-in-the-weeds, but if you have ideas or suggestions I’d enjoy a discussion 🙂

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [March 1, 2022, 11:08pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/18 "2022-03-01T23:08:35Z")

</div>

A couple of thoughts:

- In addition to LoweredCodeUtils, you might want to look at [CodeInfoTools.jl](https://github.com/JuliaCompilerPlugins/CodeInfoTools.jl), as well as other packages in the JuliaCompilerPlugins organization
- It’s also not terribly hard to traverse `CodeInfo` objects directly, e.g. see the tracer code in [Umlaut.jl](https://github.com/dfdx/Umlaut.jl/blob/main/src/trace.jl#L184-L252) which handles ~98% of Julia functions
- It’s possible to [recover loops](https://github.com/dfdx/Ghost.jl/blob/main/src/trace.jl#L572-L602) from the lowered code and represent them as [an operation with a subgraph](https://github.com/dfdx/Ghost.jl/blob/main/src/tape.jl#L398-L407) with its own inputs and outputs, thus preserving the overall DAG structure and not dealing with variable renaming and the like. The code for loop recovery in Ghost.jl is pretty tough though, and I have a better idea how to do it in Umlaut.jl, but it is still in my veeery long todo list
- To run the lowered code, I personally prefer to generate an `Expr`, compile it and call using `Base.invokelatest()`. IRTools has a [built-in function](https://fluxml.ai/IRTools.jl/latest/#Evaluating-IR-1) to run its own IR, but IIRC it goes more or less the same way

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [March 1, 2022, 11:49pm UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/19 "2022-03-01T23:49:14Z")

</div>

> [@dfdx](#):
>
> In addition to LoweredCodeUtils, you might want to look at [CodeInfoTools.jl](https://github.com/JuliaCompilerPlugins/CodeInfoTools.jl), as well as other packages in the JuliaCompilerPlugins organization

Thanks, that looks great!

> [@dfdx](#):
>
> It’s also not terribly hard to traverse `CodeInfo` objects directly, e.g. see the tracer code in [Umlaut.jl](https://github.com/dfdx/Umlaut.jl/blob/main/src/trace.jl#L184-L252) which handles ~98% of Julia functions

This looks like an interpreter, is that right? The stuff I’m doing here will be in inner loops, so it’s important to not have any extra overhead. Umlaut looks cool BTW.

> [@dfdx](#):
>
> It’s possible to [recover loops](https://github.com/dfdx/Ghost.jl/blob/main/src/trace.jl#L572-L602) from the lowered code and represent them as [an operation with a subgraph](https://github.com/dfdx/Ghost.jl/blob/main/src/tape.jl#L398-L407) with its own inputs and outputs, thus preserving the overall DAG structure and not dealing with variable renaming and the like. The code for loop recovery in Ghost.jl is pretty tough though, and I have a better idea how to do it in Umlaut.jl, but it is still in my veeery long todo list

Thanks, this sounds like a great idea.

> [@dfdx](#):
>
> To run the lowered code, I personally prefer to generate an `Expr` , compile it and call using `Base.invokelatest()` . IRTools has a [built-in function](https://fluxml.ai/IRTools.jl/latest/#Evaluating-IR-1) to run its own IR, but IIRC it goes more or less the same way

I generally try to avoid `eval`, mostly because it requires global scope. `invokelatest` also used to have some overhead, but that may not be the case any more.

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [March 2, 2022, 8:30am UTC](https://discourse.julialang.org/t/source-code-of-a-function/4499/20 "2022-03-02T08:30:55Z")

</div>

> [@cscherrer](#):
>
> This looks like an interpreter, is that right? The stuff I’m doing here will be in inner loops, so it’s important to not have any extra overhead. Umlaut looks cool BTW.

It’s a tracer - Umlaut goes through the `CodeInfo` line by line, executes each statement to get the value and records the statement and value to a DAG (tape). The tape can then be transformed and recompiled back to a normal function, so interpretation only happens once. Moreover, execution of each statement isn’t strictly required, you can as well traverse `CodeInfo` and generate `Expr` on the fly. The piece of code I linked just shows the most common cases you will encounter when traversing the `CodeInfo` object, what to do with it is completely up to you.

> `invokelatest` also used to have some overhead, but that may not be the case any more.

IIUC, `invokelatest(f, args...)` tells Julia not to link to a specific version of `f()` during compilation, but instead go to the method table and get the latest version during runtime. Basically, it’s just a dynamic dispatch, and if you call it only once for the top-level function, its cost is relatively small. Maybe code generation or pre-compilation can reduce it even further, but in my experience it’s pretty complicated, fragile and generally isn’t worth it.

[Next page](https://discourse.julialang.org/t/source-code-of-a-function/4499.md?page=2)
