# Type system abuse, advisability of

**URL:** https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390
**Category:** New to Julia
**Tags:** question, type, parametric-types
**Created:** [August 30, 2023, 9:42pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390 "2023-08-30T21:42:32Z")
**Posts on this page:** 18
**Page:** 1

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 30, 2023, 9:42pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/1 "2023-08-30T21:42:32Z")

</div>

Part of my learning process with Julia has been to abuse the type system in various fun ways to see what’s possible. My latest monstrosity uses type parameters for partial application of functions:

```julia
module TypeAbuse
struct Fix{Fn,Args,KW}
  Fix{Fn}() where {Fn} = new{Fn,(),(;)}()
  Fix{Fn,Args}() where {Fn,Args} = new{Fn,Args,(;)}()
  Fix{Fn,Args,KW}() where {Fn,Args,KW} = new{Fn,Args,KW}()
end

function (::Fix{Fn,Args,KW})(moreargs...) where {Fn,Args,KW} 
  Fn(_cat(Args, moreargs)...; KW...)
end

_cat(a, b) = (a..., b...)
end

let c = TypeAbuse.Fix{+,1}()
  @show c
  @show c(2) == 3
end

let c = TypeAbuse.Fix{digits,(),(; base = 2)}()
  println()
  @show c
  @show c(5) == [1, 0, 1]
end

```

```julia
c = Main.TypeAbuse.Fix{+, 1, NamedTuple()}()
c(2) == 3 = true

c = Main.TypeAbuse.Fix{digits, (), (base = 2,)}()
c(5) == [1, 0, 1] = true

```

I benchmarked calling `TypeAbuse.Fix{log,5}()` with `rand()` against the equivalent `Base.Fix1` call, and on my system calling both `log(5, rand())` and the `TypeAbuse.Fix` takes ~24ns, where `Base.Fix1` takes ~33ns, so it’s basically zero overhead at least in that particular case. I assume this is due to it being a singleton type and having both the function and its partially applied arguments available statically, meaning no construction of a new `Fix` instance and no field lookups?

Now, this benchmark result got me curious about how inadvisable this is, exactly. I know it will create a new type for every new `Fn`, `Args` and `KW`, increase compilation time at least to some extent, and only work with `Args`s / `KW`s that are `isbitstypes` (I think?), but considering those caveats is this actually as much of a crime against the type system as it originally seemed to me? As a beginner it feels like it could also actually be a useful optimization to shave off some nanoseconds from calling partially applied functions, assuming a case where those nanoseconds _actually_ matter and the number of different type parameters doesn’t get out of hand.

---

<div class="post-metadata">

### Author: ![tchebycheff](https://avatars.discourse-cdn.com/v4/letter/t/779978/32.png) [@tchebycheff](https://discourse.julialang.org/u/tchebycheff)
#### Post date: [August 30, 2023, 11:07pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/2 "2023-08-30T23:07:46Z")

</div>

This just hurts. I need a ritual bath.

---

<div class="post-metadata">

### Author: ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)
#### Post date: [August 30, 2023, 11:53pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/3 "2023-08-30T23:53:48Z")

</div>

Truly sublime.

It’s got me wondering if you could use a Tuple of `Val{Int}`s to mark the position of the partially applied arguments so that you could Fix arbitrary arguments and then some recursion to insert new arguments in the empty slots…

---

<div class="post-metadata">

### Author: ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)
#### Post date: [August 31, 2023, 12:08am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/4 "2023-08-31T00:08:27Z")

</div>

![image](https://global.discourse-cdn.com/julialang/original/3X/8/6/86bc847b105025e20f5e90bfaf5f78a0c4bfce72.jpeg)

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 31, 2023, 12:10am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/5 "2023-08-31T00:10:13Z")

</div>

> [@deaminase](#):
>
> meaning no construction of a new `Fix` instance and no field lookups?

If you’re comparing the `log` computations, the efficiency comes from doing a `log` call on the fixed base value at compile time. See, a logarithm with an arbitrary base is actually done by dividing two logarithms: logab = log(b)/log(a). With `Base.Fix1`, the base value a is not known at compile-time, so every run it has to retrieve the field, convert it to floating point, compute the denominator logarithm, then divide the numerator logarithm by it. With your `TypeAbuse.Fix`, the value `a` is known at compile-time, so everything except the numerator logarithm and division can be done at compile-time. You could narrow the gap by fixing a floating point base a so conversion doesn’t happen, but that’s about it.

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 12:16am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/6 "2023-08-31T00:16:17Z")

</div>

Ha, I love it that the reactions range from “why god why” to a “truly sublime” 😄

@mrufsvold, I think you could maybe use a `Tuple` of `Some` / `nothing` for that. Partially applied args like `(1, _, 2)` (`_` is just a stand-in for “not bound” here, not intended as a syntax example) could be encoded as `(Some(1), nothing, Some(2))`.

I highly encourage further development of this silliness, although I really am curious whether it’s as silly as it seems on the surface. There’s no denying that it’s not exactly something that you’d want to file under “best practices” in the Julia docs, but how bad is it when keeping in mind the caveats I listed?

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 31, 2023, 12:24am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/7 "2023-08-31T00:24:36Z")

</div>

> [@deaminase](#):
>
> how bad is it when keeping in mind the caveats I listed?

About the same as using `Val`, it’s not bad so much as niche. For the example in particular, I’d prefer doing a more direct `@eval function log5(b) log2(b)/$(log2(5)) end` however few times I need to make one (as you said, it’s not worth compiling for too many of these types).

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 12:29am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/8 "2023-08-31T00:29:40Z")

</div>

A-ha, so my gut feeling was right in that it’s actually not so much a “anybody caught doing this should be reprimanded _and_ shot” deal as it is “you probably won’t need this, but it might be useful in some very specific cases if you know what you’re doing”.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [August 31, 2023, 5:19am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/9 "2023-08-31T05:19:30Z")

</div>

This isn’t abuse of the type system, sometimes it’s useful to put data into the type domain.

Some stylistic notes:

1. usually the constructors would be organized in such a manner that there’s at most a single explicit inner constructor:

```julia
struct Fix{Fn,Args,KW}
  Fix{Fn,Args,KW}() where {Fn,Args,KW} = new{Fn,Args,KW}()
end
Fix{Fn,Args}() where {Fn,Args} = Fix{Fn,Args,(;)}()
Fix{Fn}() where {Fn} = Fix{Fn,()}()

```

1. The definition of the inner constructor in the above definition of `Fix` is actually redundant: leaving out the definition of the inner constructor would be preferable in this case because Julia will do the right thing by creating the default constructor. An inner constructor is most useful for checking the arguments given to the constructor and similar, which isn’t necessary here.

```julia-repl
julia> struct Fix{Fn,Args,KW} end

julia> methods(Fix)
# 0 methods for type constructor

julia> methods(Fix{rand})
# 0 methods for type constructor

julia> methods(Fix{rand, (), (;)})
# 1 method for type constructor:
 [1] (var"#ctor-self#"::Type{Fix{Fn, Args, KW}} where {Fn, Args, KW})()
     @ REPL[1]:1

```

1. The intention behind a constructor like `Fix{Fn}() where {Fn} = Fix{Fn,()}()` is to provide a default value for a type parameter. This makes sense and works, but may be problematic in some cases, because it is, in a sense, ambiguous, because it may not be clear whether an expression like `Fix{some_function}` should refer to the UnionAll type (see [here](https://docs.julialang.org/en/v1/base/base/#Core.UnionAll) and [here](https://docs.julialang.org/en/v1/manual/types/#UnionAll-Types)) or to the constructor method. This wouldn’t be an issue in practice for `Fix`, but it doesn’t seem like good style. It may be better to have the user-facing interface be a function that would wrap the `Fix` type, which would have only the inner constructor.

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [August 31, 2023, 5:43am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/10 "2023-08-31T05:43:33Z")

</div>

> [@deaminase](#):
>
> I benchmarked calling `TypeAbuse.Fix{log,5}()` with `rand()` against the equivalent `Base.Fix1` call, and on my system calling both `log(5, rand())` and the `TypeAbuse.Fix` takes ~24ns, where `Base.Fix1` takes ~33ns, so it’s basically zero overhead at least in that particular case.

I’m curious how you benchmarked this, can you share the code?

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [August 31, 2023, 11:04am UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/11 "2023-08-31T11:04:17Z")

</div>

> [@deaminase](#):
>
> is this actually as much of a crime against the type system as it originally seemed to me? As a beginner it feels like it could also actually be a useful optimization to shave off some nanoseconds from calling partially applied functions

Two remarks:

1. I don’t see this kind of code as type system abuse at all. This is pretty much run-of-the-mill stuff, you see lot more complex manipulations in type space in Base and the standard libraries.

2. That said, I don’t understand why you think it optimizes anything. `Fn` will get called, maybe if it is super-simple then you gain something from constant folding, but that is not generally guaranteed. You may want to read the discussion that lead to `Base.Fix2` (`Base.Fix1` was introduced after to make it symmetric) for motivation, it does not generally lead to any _runtime_ saving.

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 12:38pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/12 "2023-08-31T12:38:45Z")

</div>

Sure! I actually hadn’t thought about whether I might be benchmarking this wrong, but this is what I used:

```julia
let bset = @benchmarkset "perf" begin
    let c = TypeAbuse.Fix{log,5}()
      @case "$c" $c(rand()) evals = 1000 samples = 10000
    end
    let c = Base.Fix1(log, 5)
      @case "$c" $c(rand()) evals = 1000 samples = 10000
    end

    @case "log(5,rand())" log(5, rand()) evals = 1000 samples = 10000
  end

  for (_, set) in BenchmarkTools.run(bset)
    for (name, trial) in set
      display(name)
      display(trial)
    end
  end

```

On my M1 Mac those give `log(5,rand())` median ~25ns, `Base.Fix1{typeof(log), Int64}(log, 5)` ~34ns, `Main.TypeAbuse.Fix{log, 5, NamedTuple()}()` ~25ns.

@Tamas_Papp, ☝ that is what I was referring to when I said that (at least at face value and to a beginner) it looks like `TypeAbuse.Fix` can have less overhead compared to `Base.Fix1` in some cases.

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 12:55pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/13 "2023-08-31T12:55:20Z")

</div>

Ah, thank you for the “best practices” note. I’m honestly still a bit puzzled about how types (especially `UnionAll`s) and constructor function naming play together.

As an example, would the `Fix` in `Fix() = Fix{identity}()` be referring to the (`UnionAll`) type `Fix` or is it just a method that happens to be named `Fix`? And how does that compare to eg. `(::Type{Fix})() = Fix{identity}()`?

`Fix{Fn}() where {Fn} = # …` seems like it’s definitely referring to the type, so is _that_ the same as `(::Type{Fix{Fn}})() where {Fn} = # …`?

You also mentioned potential ambiguity between constructor methods and types, and I’m fairly sure I’ve run into that in some tomfoolery but I can’t quite remember what it was. It was related to providing default type parameters though, possibly in a constructor with no type parameters

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [August 31, 2023, 1:04pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/14 "2023-08-31T13:04:50Z")

</div>

> [@deaminase](#):
>
> On my M1 Mac those give `log(5,rand())` median ~25ns, `Base.Fix1{typeof(log), Int64}(log, 5)` ~34ns, `Main.TypeAbuse.Fix{log, 5, NamedTuple()}()` ~25ns.

It’s definitely machine dependent then - on my machine, I see these:

```julia
"log(5,rand())"
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 14.840 ns … 23.990 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 15.060 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 15.104 ns ± 0.407 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

      ▂▅██▇▃                                                   
  ▂▃▄▆███████▅▃▃▂▂▂▂▁▁▂▂▂▂▂▂▂▁▂▁▁▁▁▁▁▂▁▂▂▁▁▂▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▂ ▃
  14.8 ns Histogram: frequency by time 16.6 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
"Base.Fix1{typeof(log), Int64}(log, 5)"
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 8.230 ns … 19.890 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 8.430 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 8.455 ns ± 0.329 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

                 █▁▁▁ ▁                                      
  ▂▁▂▂▂▂▃▃▃▃█▆▇███████▇█▅▄▃▃▃▂▂▂▂▂▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂ ▃
  8.23 ns Histogram: frequency by time 8.92 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
"Main.TypeAbuse.Fix{log, 5, NamedTuple()}()"
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 14.850 ns … 25.340 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 15.080 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 15.114 ns ± 0.391 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

        ▁▃▆███▇▅▃▁                                             
  ▂▂▃▃▅▆██████████▇▅▄▃▃▂▂▂▂▂▂▁▁▂▁▁▂▂▂▁▂▂▂▂▂▂▂▂▁▁▁▁▁▁▁▁▂▁▁▁▂▁▂ ▃
  14.8 ns Histogram: frequency by time 16 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

So `Fix1` is actually fastest here:

```julia
julia> versioninfo()
Julia Version 1.11.0-DEV.269
Commit f8d46800c6c (2023-08-12 15:16 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: 24 × AMD Ryzen 9 7900X 12-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-15.0.7 (ORCJIT, znver3)
  Threads: 34 on 24 virtual cores
Environment:
  JULIA_PKG_USE_CLI_GIT = true

```

Generally speaking, any possible advantage of your `TyepAbuse` version of `Fix1` is only possible with `isbits` values, due to being able to use them as type parameters. This means that you’re more or less forcing the compiler to propagate these constants (or risk type instability if it can’t). There can be cases where this is faster at runtime, but don’t overdo it - the additional constant propagation can come with an increase in compilation time, if done excessively.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [August 31, 2023, 1:27pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/15 "2023-08-31T13:27:51Z")

</div>

This REPL session should be illustrative. Start Julia with `--warn-overwrite=yes` (a good idea anyway) to get the “method overwritten” warning:

```julia-repl
julia> struct S{x} end

julia> S() = S{3}()
S

julia> methods(S)
# 1 method for type constructor:
 [1] S()
     @ REPL[2]:1

julia> (::Type{S})() = S{2}()
WARNING: Method definition (::Type{Main.S{x} where x})() in module Main at REPL[2]:1 overwritten at REPL[4]:1.

```

Conclusion: `S() = S{3}()` is syntax sugar for `(::Type{S})() = S{3}()`. So this is a method defined on the UnionAll type.

> [@deaminase](#):
>
> potential ambiguity between constructor methods and types

One place where this may matter in particular is when calling functions like `map` with arguments that are types. `map` currently (it may not necessarily be a set matter) assumes that when it is given a type as the first argument, that type will be the element type of the collection. This is usually problematic when it is given an UnionAll type, because the final result ends up not being concrete.

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 1:51pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/16 "2023-08-31T13:51:22Z")

</div>

Interesting that it’s machine-dependent! I wonder what’s going on under the hood; I somehow intuitively didn’t expect `TypeAbuse.Fix` could be slower than `Base.Fix1`, and assumed that it would at worst break even.

> Generally speaking, any possible advantage of your `TyepAbuse` version of `Fix1` is only possible with `isbits` values, due to being able to use them as type parameters. This means that you’re more or less forcing the compiler to propagate these constants (or risk type instability if it can’t). There can be cases where this is faster at runtime, but don’t overdo it - the additional constant propagation can come with an increase in compilation time, if done excessively.

Oh yeah, this was purely a thought exercise that I did “because it’s there” 😁 Those were the caveats I was more or less assuming it’d have, I just wasn’t sure if there would be something more (“this will summon nasal demons”). Sort of assumed not, because – as others have also pointed out here – using type parameters for storing static values is a common practice in Julia and it’s fairly visible in many packages.

**Edit:** I did the benchmarks without low power mode and the plain function call and the `TypeAbuse.Fix` call still take more or less exactly the same time, ~6ns, where `Base.Fix1` is ~10ns

---

<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: [August 31, 2023, 4:14pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/17 "2023-08-31T16:14:48Z")

</div>

Very cool idea. Here’s one possible explanation:

```julia
julia> const f = Base.Fix1(log, 5.0)
(::Base.Fix1{typeof(log), Float64}) (generic function with 1 method)

julia> const g = TypeAbuse.Fix{log, 5.0}()
Main.TypeAbuse.Fix{log, 5.0, NamedTuple()}()

julia> @code_typed optimize=true f(0.2)
CodeInfo(
1 ─ %1 = Base.getfield(f, :x)::Float64
│ %2 = invoke Base.Math._log(y::Float64, $(QuoteNode(Val{:ℯ}()))::Val{:ℯ}, :log::Symbol)::Float64
│ %3 = invoke Base.Math._log(%1::Float64, $(QuoteNode(Val{:ℯ}()))::Val{:ℯ}, :log::Symbol)::Float64
│ %4 = Base.div_float(%2, %3)::Float64
└── return %4
) => Float64

julia> @code_typed optimize=true g(0.2)
CodeInfo(
1 ─ %1 = Core.getfield(moreargs, 1)::Float64
│ %2 = invoke Base.Math._log(%1::Float64, $(QuoteNode(Val{:ℯ}()))::Val{:ℯ}, :log::Symbol)::Float64
│ %3 = Base.div_float(%2, 1.6094379124341003)::Float64
└── return %3
) => Float64

```

In `g`, `5.0` is at the type level, which guarantees it’s known statically. That’s not the case in `f`.

This is basically pitting type-level programming against constant propagation. As I understand, this can be hard to benchmark, since the compiler is stateful. Maybe someone with a deeper understanding of the compiler can add more detail about this.

I’ve used the type-level approach before and kind of enjoy it, but there can be some problems:

1. The compiler is not at all optimized for dealing with large numbers of types. If you had lots of different `Base.Fix` calls you’d probably be fine, but lots of different `TypeAbuse.Fix` instances would (I think) bog down the compiler.
2. The core dev team has put lots of effort into constant propagation. As I understand, this is also ongoing work, so I think we can expect even more progress on this front. Maybe not so for type inference, at least not in the short term.

FWIW, you can get the same benefits (and potential issues) as your approach like this:

```julia
julia> using Static
[Info: Precompiling Static [aedffcd0-7271-4cad-89d0-dc628f76c6d3]

julia> const h = Base.Fix1(log, static(5.0))
(::Base.Fix1{typeof(log), StaticFloat64{5.0}}) (generic function with 1 method)

julia> @code_typed optimize=true h(0.2)
CodeInfo(
1 ─ %1 = invoke Base.Math._log(y::Float64, $(QuoteNode(Val{:ℯ}()))::Val{:ℯ}, :log::Symbol)::Float64
│ %2 = Base.div_float(%1, 1.6094379124341003)::Float64
└── return %2
) => Float64

```

---

<div class="post-metadata">

### Author: ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)
#### Post date: [August 31, 2023, 4:25pm UTC](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/18 "2023-08-31T16:25:36Z")

</div>

Yeah I also looked at the lowered code after Benny’s earlier comment and noted the same thing, which is why it’s so surprising that on @Sukera system `TypeAbuse.Fix{log, 5, NamedTuple()}()` was _slower_ than the equivalent `Base.Fix1` (see benchmark code above if you want to try it out). I assumed that _at worst_ these simple `TypeAbuse.Fix` uses would lower to something that’s more or less equivalent to their `Base.Fix1` counterparts and have comparable performance.

My `versioninfo()` is:

```julia
Julia Version 1.9.3
Commit bed2cd540a (2023-08-24 14:43 UTC)
Build Info:
  Built by Homebrew (v1.9.3)

    Note: This is an unofficial build, please report bugs to the project
    responsible for this build and not to the Julia project unless you can
    reproduce the issue using official builds available at https://julialang.org/downloads

Platform Info:
  OS: macOS (arm64-apple-darwin22.4.0)
  CPU: 10 × Apple M1 Pro
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-14.0.6 (ORCJIT, apple-m1)
  Threads: 8 on 8 virtual cores

```
