# \`Unitful\`: define units in module and use them outside

**URL:** <https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059>\
**Category:** General Usage\
**Tags:** question, unitful\
**Created:** [December 26, 2023, 3:22pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059 "2023-12-26T15:22:35Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [December 26, 2023, 3:22pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/1 "2023-12-26T15:22:35Z")

</div>

I’m reading the documentation (linked at the bottom of this message) about how to define new units and have come up with a module like this

```julia
module MyUnits
using Unitful

Unitful.register(@ __MODULE__ ) # What does this do?

@unit pt "pt" pt (1//72)*u"inch" false

const mm = u"mm"
const cm = u"cm"
const pt = u"pt"
end

```

and use this module in the main program:

```julia
push!(LOAD_PATH,pwd())
using MyUnits: mm, pt

println(1mm/1pt) # -> 1.0 mm pt⁻¹
println(1mm/1pt + 0.0) # -> ERROR: LoadError: KeyError: key :pt not found

```

What is going wrong?

> **[Defining new units · Unitful.jl](https://painterqubits.github.io/Unitful.jl/stable/newunits/)**
>
> Documentation for Unitful.jl.

---

<div class="post-metadata">

**Author:** ![mike.ingold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike.ingold/32/203749_2.png) [@mike.ingold](https://discourse.julialang.org/u/mike.ingold)\
**Post date:** [December 26, 2023, 4:23pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/2 "2023-12-26T16:23:16Z")

</div>

Seems to work fine for me in REPL (Julia v1.9.4, Unitful v1.19.0).

```julia
julia> module MyUnits
       using Unitful

       Unitful.register(@ __MODULE__ ) # What does this do?

       @unit pt "pt" pt (1//72)*u"inch" false

       const mm = u"mm"
       const cm = u"cm"
       const pt = u"pt"
   end
Main.MyUnits

julia> using .MyUnits: mm, pt

julia> println(1mm/1pt)
1.0 mm pt^-1

julia> println(1mm/1pt + 0.0)
2.8346456692913384

```

---

<div class="post-metadata">

**Author:** ![mike.ingold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike.ingold/32/203749_2.png) [@mike.ingold](https://discourse.julialang.org/u/mike.ingold)\
**Post date:** [December 26, 2023, 4:27pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/3 "2023-12-26T16:27:24Z")

</div>

Try these same lines in REPL like I did above. If it still fails, then maybe there’s something different about the environments or package versions that’s responsible for the issue. If it works for you in REPL, then the issue must have something to do with either how the module file is written/defined or something to do with how it’s loaded.

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [December 26, 2023, 6:19pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/4 "2023-12-26T18:19:18Z")

</div>

> [@mike.ingold](#):
>
> Try these same lines in REPL like I did above.

It _does_ work! But it fails as below

```julia
julia> include("mainprogram.jl")
[Info: Precompiling MyUnits [top-level]
1.0 mm pt⁻¹
ERROR: LoadError: KeyError: key :pt not found
Stacktrace:
  [1] getindex(h::Dict{Symbol, Tuple{Float64, Rational{Int64}}}, key::Symbol)
    @ Base ./dict.jl:484
  [2] basefactor
    @ ~/.julia/packages/Unitful/R4J37/src/units.jl:262 [inlined]
  [3] map
    @ ./tuple.jl:274 [inlined]
  [4] basefactor(#unused#::Unitful.FreeUnits{(mm, pt⁻¹), NoDims, nothing})
    @ Unitful ~/.julia/packages/Unitful/R4J37/src/units.jl:265
  [5] #s103#141
    @ ~/.julia/packages/Unitful/R4J37/src/conversion.jl:13 [inlined]
  [6] var"#s103#141"(::Any, s::Any, t::Any)
    @ Unitful ./none:0
  [7] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any})
    @ Core ./boot.jl:602
  [8] promote_rule
    @ ~/.julia/packages/Unitful/R4J37/src/promotion.jl:99 [inlined]
  [9] promote_type
    @ ./promotion.jl:307 [inlined]
 [10] _promote
    @ ./promotion.jl:357 [inlined]
 [11] promote
    @ ./promotion.jl:381 [inlined]
 [12] +(x::Unitful.Quantity{Float64, NoDims, Unitful.FreeUnits{(mm, pt⁻¹), NoDims, nothing}}, y::Float64)
    @ Base ./promotion.jl:410
 [13] top-level scope
    @ . . . mainprogram.jl:5
 [14] include(fname::String)
    @ Base.MainInclude ./client.jl:478
 [15] top-level scope
    @ REPL[1]:1
in expression starting at . . . /mainprogram.jl:5

julia>

```

The main program is

```julia
push!(LOAD_PATH,pwd())
using MyUnits: mm, pt

println(1mm/1pt) # -> 1.0 mm pt⁻¹
println(1mm/1pt + 0.0) # -> ERROR: LoadError: KeyError: key :pt not found

```

`MyUnits.jl` and the main program are in the same directory and I run the REPL in the same directory.

Julia Version 1.9.4 on macOS 14.2.1 .

---

<div class="post-metadata">

**Author:** ![mike.ingold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike.ingold/32/203749_2.png) [@mike.ingold](https://discourse.julialang.org/u/mike.ingold)\
**Post date:** [December 26, 2023, 8:20pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/5 "2023-12-26T20:20:34Z")

</div>

I just tried putting the module contents above into a `MyUnits.jl` file, `include`’ing that file, and then running the two `println` tests: still no issue.

Seems like something about `using` the module from the `LOAD_PATH` is causing the issue.

```julia
(@v1.9) pkg> activate --temp
  Activating new project at `C:\Users\x\AppData\Local\Temp\jl_Nmrvor`

julia> cd("C:\\Users\\x\\AppData\\Local\\Temp\\jl_Nmrvor")

julia> push!(LOAD_PATH,pwd())
4-element Vector{String}:
 "@"
 "@v#.#"
 "@stdlib"
 "C:\\Users\\x\\AppData\\Local\\Temp\\jl_Nmrvor"

julia> using MyUnits: mm, pt
[Info: Precompiling MyUnits [top-level]

julia> println(1mm/1pt)
1.0 mm pt^-1

julia> println(1mm/1pt + 0.0)
ERROR: KeyError: key :pt not found
Stacktrace:
  [1] getindex(h::Dict{Symbol, Tuple{Float64, Rational{Int64}}}, key::Symbol)
    @ Base .\dict.jl:484
  [2] basefactor
    @ C:\Users\x\.julia\packages\Unitful\R4J37\src\units.jl:262 [inlined]
  [3] map
    @ .\tuple.jl:274 [inlined]
  [4] basefactor(#unused#::Unitful.FreeUnits{(mm, pt^-1), NoDims, nothing})
    @ Unitful C:\Users\x\.julia\packages\Unitful\R4J37\src\units.jl:265
  [5] #s103#141
    @ C:\Users\x\.julia\packages\Unitful\R4J37\src\conversion.jl:13 [inlined]
  [6] var"#s103#141"(::Any, s::Any, t::Any)
    @ Unitful .\none:0
  [7] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any})
    @ Core .\boot.jl:602
  [8] promote_rule
    @ C:\Users\x\.julia\packages\Unitful\R4J37\src\promotion.jl:99 [inlined]
  [9] promote_type
    @ .\promotion.jl:307 [inlined]
 [10] _promote
    @ .\promotion.jl:357 [inlined]
 [11] promote
    @ .\promotion.jl:381 [inlined]
 [12] +(x::Unitful.Quantity{Float64, NoDims, Unitful.FreeUnits{(mm, pt^-1), NoDims, nothing}}, y::Float64)
    @ Base .\promotion.jl:410
 [13] top-level scope
    @ REPL[7]:1

```

I don’t know much about the mechanics beneath and don’t see any obvious indications in [the Module docs](https://docs.julialang.org/en/v1/manual/modules/) that would tell me to expect a difference in behavior between these loading styles. I don’t know whether this would be a bug in Unitful, if it’s the intended behavior, or some nuance in Julia’s package loading logic that I don’t understand.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 26, 2023, 9:32pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/6 "2023-12-26T21:32:34Z")

</div>

The issue seems to be the value of `@ __MODULE__ ` which is `Main.MyUnits` in the successful load paths and just `MyUnits` in the erroring paths.  
Still investigating, but maybe someone else already gets the problem.

---

<div class="post-metadata">

**Author:** ![mike.ingold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike.ingold/32/203749_2.png) [@mike.ingold](https://discourse.julialang.org/u/mike.ingold)\
**Post date:** [December 27, 2023, 12:18am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/7 "2023-12-27T00:18:35Z")

</div>

I found a newer example [here in the Unitful docstring](https://painterqubits.github.io/Unitful.jl/stable/manipulations/#Unitful.register) that makes me think it’s something to do with how this is implemented in Unitful.

Based on [the Module docs here](https://docs.julialang.org/en/v1/manual/modules/#Module-initialization-and-precompilation), a function ` __init__ ()` in a module is executed at runtime vs compile time… I’m not sure I quite understand this distinction because working this way I get the error at precompile time.

I tried defining a `MyUnits.jl` file, per the `Unitful.register` docstring linked above:

```julia
module MyUnits
	using Unitful

	@unit pt "pt" pt (1//72)*u"inch" false

	const mm = u"mm"
	const cm = u"cm"
	const pt = u"pt"
	
	function __init__ ()
		Unitful.register(MyUnits)
	end
end

```

Now I get the following error, instead.

```julia
julia> using MyUnits: mm, pt
[Info: Precompiling MyUnits [top-level]
ERROR: LoadError: ArgumentError: Symbol pt could not be found in unit modules Module[Unitful]
Stacktrace:
 [1] lookup_units(unitmods::Vector{Module}, sym::Symbol)
   @ Unitful C:\Users\x\.julia\packages\Unitful\R4J37\src\user.jl:707
 [2] var"@u_str"( __source__ ::LineNumberNode, __module__ ::Module, unit::Any)
   @ Unitful C:\Users\x\.julia\packages\Unitful\R4J37\src\user.jl:639
 [3] include
   @ .\Base.jl:457 [inlined]
 [4] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
   @ Base .\loading.jl:2049
 [5] top-level scope
   @ stdin:3
in expression starting at C:\Users\x\AppData\Local\Temp\jl_znDJGl\MyUnits.jl:8
in expression starting at C:\Users\x\AppData\Local\Temp\jl_znDJGl\MyUnits.jl:1
in expression starting at stdin:3
ERROR: Failed to precompile MyUnits [top-level] to "C:\\Users\\x\\.julia\\compiled\\v1.9\\jl_9F38.tmp".
Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, keep_loaded_modules::Bool)
   @ Base .\loading.jl:2294
 [3] compilecache
   @ .\loading.jl:2167 [inlined]
 [4] _require(pkg::Base.PkgId, env::String)
   @ Base .\loading.jl:1805
 [5] _require_prelocked(uuidkey::Base.PkgId, env::String)
   @ Base .\loading.jl:1660
 [6] macro expansion
   @ .\loading.jl:1648 [inlined]
 [7] macro expansion
   @ .\lock.jl:267 [inlined]
 [8] require(into::Module, mod::Symbol)
   @ Base .\loading.jl:1611

```

I’m a moderate user of **Unitful.jl** and have made significant use of it in my own packages, but I’ve always been a little puzzled by the unit definition/registration system. Now, TIL ` __init__ ` module functions are a thing. The more you know, I guess.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 27, 2023, 2:35am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/8 "2023-12-27T02:35:20Z")

</div>

I think you want to base your units module on:

> <https://github.com/JuliaGeodynamics/GeoParams.jl/blob/v0.4.4/src/Units.jl>

There are a few things to note:

- `@unit` creates a local binding to `pt`, so `const pt = u"pt"` is not necessary.
- To have `pt`, `mm`, `cm` available in the enclosing module you want to export them.

Something like:

```julia
module MyUnits
    using Unitful
    export pt, mm, cm
    @unit pt "pt" pt (1//72)*u"inch" false

    const mm = u"mm"
    const cm = u"cm"

    Unitful.register(@ __MODULE__ )
    function __init__ ()
        return Unitful.register(@ __MODULE__ )
    end               
end

```

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [December 27, 2023, 4:33am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/9 "2023-12-27T04:33:37Z")

</div>

> [@Dan](#):
>
> ```julia
> module MyUnits
> . . .
> Unitful.register(@ __MODULE__ )
> function __init__ ()
> return Unitful.register(@ __MODULE__ )
> end
> 
> ```

Thank you!!! That’s it. At the bottom of this message, I show a complete example that works, using this method. [Could somebody add this to the documentation? If I were familiar with github, I’d create a PR, but each time I try to start github, I realize the steep initial learning curve and have to give it up. I’m not a developer and I hardly has a motivation to master github except for this situation.]

Before showing the code,

> [@Dan](#):
>
> To have `pt`, `mm`, `cm` available in the enclosing module you want to export them.

I deliberately don’t do so for this particular case. I don’t want to spill little symbols like `mm` into my code without explicitly declaring them. If you included `export pt, mm, cm` in your module, you get this result:

```julia
using MyUnits # -> implicitly exports pt, mm, cm
nn = 92
mm = 9 # I wanted to use this symbol!
. . .

```

This problem is likely to get bigger in the future as I keep adding custom units and convenience symbols to `MyUnits.jl`. So I choose between

```julia
using MyUnits: pt # when I use pt often

```

and

```julia
using MyUnits
MU = MyUnits
. . .
a = 3(MU.pt) # -> 3 pt

```

* * *

The module file that works is

```julia
module MyUnits
using Unitful

@unit pt "pt" pt (1//72)*u"inch" false

const mm = u"mm"
const cm = u"cm"
#const pt = u"pt" # not necessary, because @unit does that.

Unitful.register(@ __MODULE__ )
function __init__ ()
  return Unitful.register(@ __MODULE__ )
end

end

```

and the main program that works is

```julia
push!(LOAD_PATH,pwd())
using MyUnits: mm, pt
println(1mm/1pt) # -> 1.0 mm pt⁻¹
println(1mm/1pt + 0.0) # -> 2.8346456692913384

```

---

<div class="post-metadata">

**Author:** ![cmichelenstrofer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cmichelenstrofer/32/44392_2.png) [@cmichelenstrofer](https://discourse.julialang.org/u/cmichelenstrofer)\
**Post date:** [December 27, 2023, 5:33am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/10 "2023-12-27T05:33:14Z")

</div>

This is explained in the documentation [here](https://painterqubits.github.io/Unitful.jl/stable/extending/#Precompilation), but indeed providing it explicitly as something like below would be beneficial! ~~Also maybe providing a link to this page from the “[Defining new units](https://painterqubits.github.io/Unitful.jl/stable/newunits/#Defining-new-units)” section.~~

```julia
module YourModule
    # define new units here 

    # Unitful.register(YourModule) # uncomment if you use u-strings with newly defined units
    function __init__ ()
        return Unitful.register(YourModule)
    end
end

```

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [December 27, 2023, 6:07am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/11 "2023-12-27T06:07:18Z")

</div>

> [@cmichelenstrofer](#):
>
> ```julia
> module YourModule
> # define new units here 
> 
> Unitful.register(YourModule)
> function __init__ ()
> return Unitful.register(YourModule)
> end
> end
> 
> ```

Would it be even possible to provide a macro?

```julia
module YourModule
# define new units here
@register_all
end

```

I’m not so familiar with macros as to be able to tell whether this is even possible or not.

---

<div class="post-metadata">

**Author:** ![cmichelenstrofer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cmichelenstrofer/32/44392_2.png) [@cmichelenstrofer](https://discourse.julialang.org/u/cmichelenstrofer)\
**Post date:** [December 27, 2023, 6:47am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/12 "2023-12-27T06:47:07Z")

</div>

> [@cmichelenstrofer](#):
>
> Also maybe providing a link to this page from the “[Defining new units](https://painterqubits.github.io/Unitful.jl/stable/newunits/#Defining-new-units)” section.

there is such a link there… my bad.

> [@ryofurue](#):
>
> Would it be even possible to provide a macro?

I don’t know that using a macro for defining the `init` function (if even possible) is a good idea. since it would hide this function. I think that requiring packages to include the `register` line in their `init` function is not a big deal.

Also, I think you might be OK without the call to `register` in the main body of the Module since you never use u-strings with the newly defined units (e.g. you never say `u"pt"`).

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [December 27, 2023, 9:07am UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/13 "2023-12-27T09:07:44Z")

</div>

> [@cmichelenstrofer](#):
>
> I think that requiring packages to include the `register` line in their `init` function is not a big deal.
> 
> Also, I think you might be OK without the call to `register` in the main body of the Module since you never use u-strings with the newly defined units (e.g. you never say `u"pt"`).

The problem is that the current `Unitful` package requires such obscure knowledge as _that_, on the part of the “mere” user. To understand what you have just said, I would have to read the documentation and understand what ` __init__ ` does and what `register` does.

If you read through this thread, you realize some other users also failed to fully understand how `register` works. The current documentation around ` __init__ ` tries to give the knowledge, but what the “mere” user needs is a simple piece of code that works without having to know how `Unitful` is internally implemented.

```julia
I don’t know that using a macro for defining the init function (if even possible) is a good idea. since it would hide this function. 

```

If the user wants to define her/his own init function, s/he must be more knowledgeable than the average user. So, this might be fine for such a user:

```julia
@register_all begin
   # Write the contents of __init__
end

```

whereas for a user who doesn’t need to think about ` __init__ `

```julia
@register_all

```

Of course, I’m just imagining things without having much knowledge about macros.

---

<div class="post-metadata">

**Author:** ![cmichelenstrofer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cmichelenstrofer/32/44392_2.png) [@cmichelenstrofer](https://discourse.julialang.org/u/cmichelenstrofer)\
**Post date:** [December 27, 2023, 10:14pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/14 "2023-12-27T22:14:45Z")

</div>

I am not a `Unitful` maintainer; if you feel strongly about this I recommend creating a GitHub issue in their repo. But here are my two cents:

What is “_obscure_” and what is a “_mere user_”? These depend greatly on your own learning path. I would say that if you are trying to create a module you are a step beyond the most basic user (one that uses modules but not creates new ones) and you have the extra burden of learning how modules work, like knowing that modules can have an ` __init__ ` function. Both the [documentation on modules](https://docs.julialang.org/en/v1/manual/modules/#Module-initialization-and-precompilation), and using this forum are good ways to learn it, as you have done. You now know this and can use this knowledge whenever you create new modules. The `init` function is something related to modules, and I don’t think that Unitful documentation is the right place to explain it, or that Unitufl should provide ways around it. They should explain _ **why** _ you need to use `register` in your `init`. If you think this is not explained clearly you should create an issue and/or pull request on their GitHub site. Documentation can always be improved! But creating a macro to avoid creating the `init` function is not the right move IMO. Note that the only “extra” when creating a module that defines new units is a single line in your `init` function! Once you know this and understand why it is required it should not be any more difficult than using a macro and is much clearer.

---

<div class="post-metadata">

**Author:** ![mike.ingold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike.ingold/32/203749_2.png) [@mike.ingold](https://discourse.julialang.org/u/mike.ingold)\
**Post date:** [December 27, 2023, 10:38pm UTC](https://discourse.julialang.org/t/unitful-define-units-in-module-and-use-them-outside/108059/15 "2023-12-27T22:38:17Z")

</div>

> [@cmichelenstrofer](#):
>
> The `init` function is something related to modules, and I don’t think that Unitful documentation is the right place to explain it, or that Unitufl should provide ways around it. They should explain _ **why** _ you need to use `register` in your `init`. If you think this is not explained clearly you should create an issue and/or pull request on their GitHub site. Documentation can always be improved! But creating a macro to avoid creating the `init` function is not the right move IMO. Note that the only “extra” when creating a module that defines new units is a single line in your `init` function! Once you know this and understand why it is required it should not be any more difficult than using a macro and is much clearer.

I pretty much concur with this. The issue for me was mostly in discovery of the documentation for what code snippet would be required, not that code itself. I’ve got a couple of focused Julia packages and somehow haven’t yet had a need to implement an ` __init__ ` function for one, but it was easy enough to find them listed in the Module docs. If anything, they could’ve also just linked to that part of the docs so that it’s obvious that’s a function with a special purpose.
