# Understanding Primitive and Composite Types

**URL:** <https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477>\
**Category:** New to Julia\
**Created:** [December 10, 2025, 4:41pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477 "2025-12-10T16:41:53Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Queenvictoria](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/queenvictoria/32/218740_2.png) [@Queenvictoria](https://discourse.julialang.org/u/Queenvictoria)\
**Post date:** [December 10, 2025, 4:41pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/1 "2025-12-10T16:41:53Z")

</div>

I’m trying to understand the different types, but it isn’t completely clear.

As far as I understand, primitive types are those that contain one actual value. So are all concrete types, that aren’t containers, primitive types? Did I simplify it too much?

As for composite types, are they exclusively user defined? Or is there an existing type in Julia that qualifies as a composite type?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [December 10, 2025, 4:56pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/2 "2025-12-10T16:56:18Z")

</div>

almost all types are composite types. (and almost all types defined in Julia are “user” defined in the sense that they are defined in normal julia without any special compiler support).

The real difference between primitive and composite types is whether they are built out of a single region of pure bits or other types. An example of the former is `Int64`. it has no constituent parts, and the basic operations you can perform on it are described in the language as (essentially) specific LLVM code. However, most types are built out of other types (e.g. `Rational`) where operations on a `Rational` are written in terms of operations on the numerator and denominator.

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [December 10, 2025, 5:39pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/3 "2025-12-10T17:39:54Z")

</div>

To add:

The tremendous majority of Julia users (and developers) will never ever define a `primitive type`. There are only a handful of them in the Base language (`Base.BitInteger`, `Base.IEEEFloat`, `Bool`, `Char`, and probably something related to `Ptr` are the ones that come to mind) and I’ve never seen one defined in a package until I found Quadmath.jl researching this post (which adds `Float128` as a primitive via library support).

If a computer doesn’t have hardware-native instructions (or, less commonly, software emulation libraries) that support the type, it probably isn’t a primitive. And Julia has already covered most of the common hardware types. The main reason I would imagine needing a new primitive type is if you were using non-IEEE-754 floats (like some of the [nonstandard flavors of 16 bit floats](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) or anything smaller) or longer IEEE floats. Or if you had some hardware or library that natively supported longer fixed-size integer types.

Another useful distinction you might be searching for is [`isbitstype`](https://docs.julialang.org/en/v1/base/base/#Base.isbitstype).

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [December 10, 2025, 7:04pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/4 "2025-12-10T19:04:06Z")

</div>

The Quadmath case is actually quite interesting (and recent, this was only introduced ~2 months ago in [cleanups by oscardssmith · Pull Request #92 · JuliaMath/Quadmath.jl · GitHub](https://github.com/JuliaMath/Quadmath.jl/pull/92)). It turns out that the Julia compiler is smart enough to pass primitive types that are AbstractFloats using floating point calling conventions rather than normal calling conventions (specifically using xmm registers on x86), so we were able to save a fair bit of complexity by just telling Julia to treat the `Float128` as a bunch of bits.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [December 12, 2025, 11:30pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/5 "2025-12-12T23:30:26Z")

</div>

> [@Queenvictoria](#):
>
> As far as I understand, primitive types are those that contain one actual value. So are all concrete types, that aren’t containers, primitive types? Did I simplify it too much?
> 
> As for composite types, are they exclusively user defined? Or is there an existing type in Julia that qualifies as a composite type?

What’s not obvious for julia users is that julia comes out of the box with very little. Almost everything you use in a julia program has been defined in the module `Base` in terms of simpler julia constructs. Even `Float64` and `Int16` aren’t built in. These types are primitive types, most of them defined in `Base` (a couple of them have been predefined (like `Int`) because they’re needed (if you write `64` it has to end up somewhere).

So, somewhere in these `Base` definitions there are things like,

```julia-auto
primitive type Int32 32 end
primitive type Float64 64 end

```

(In [julia/base/boot.jl at master · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/master/base/boot.jl) and [julia/base/int.jl at master · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/master/base/int.jl))  
You can also create your own primitive types, but there are very few opportunities to operate on them. In `Base`, functions like `+` and `*` on these primitive types are defined by calling “intrinsic” functions (located in `Core.Intrinsics`), which are not written in julia, but in C. Calling them isn’t really like calling a function, but when a julia function calling an intrinsic is compiled, the intrinsic “call” is “inlined”, i.e. it just emits machine code (or, rather, LLVM-code).

If you define your own primitive type, in order to operate on them, or even create an object, you must somehow call an intrinsic, or some C-code, or use `reinterpret` (which calls the intrinsic `bitcast`):

```julia-auto
julia> primitive type MyInt 24 end
julia> a = reinterpret(MyInt, (UInt8(1), UInt8(2), UInt8(3)))
MyInt(0x030201)

# define a + for them:
julia> x::MyInt + y::MyInt = Core.Intrinsics.add_int(x,y) # this will mess up you julia session
+ (generic function with 1 method)

julia> b = a + a
MyInt(0x060402)

julia> reinterpret(NTuple{3,UInt8}, b)
(0x02, 0x04, 0x06)

```

Similar incantations are invoked to define arithmetic and logic operations on the primitive types in `Base`, so that you can write `2+14.3` and similar.

Normally there’s no reason to define primitive types yourself. Some special packages do it, like BitIntegers.jl, which can be used to define e.g. 3-byte integers as above, with all the operations normally used on integers. Very similar to what `Base` does with `Int`s and `Float`s.

Except for these few primitive types in `Base` (including those you can define by `@enum`), everything else are composite types (`Vector`, `Complex`, `Rational`, `IOStream`, `ReentrantLock` etc, etc) defined in terms of other composite types, or primitive types. You can see their layout with `dump`:

```julia-auto
julia> dump(IOStream)
mutable struct IOStream <: IO
  handle::Ptr{Nothing}
  ios::Vector{UInt8}
  name::String
  mark::Int64
  lock::ReentrantLock
  _dolock::Bool

```

A concrete type can be either primitive or composite. What distinguishes it from an abstract type is that a concrete type has a memory representation and can be instantiated as an object whereas an abstract type is merely an organizational element in the type universe, possibly with subtypes (which can be either concrete or abstract).

---

<div class="post-metadata">

**Author:** ![raman\_kumar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raman_kumar/32/26782_2.png) [@raman\_kumar](https://discourse.julialang.org/u/raman_kumar)\
**Post date:** [December 12, 2025, 11:51pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/6 "2025-12-12T23:51:13Z")

</div>

Yes, It is difficult to understand by beginners. Earlier I also have raised issue [Julia Type system manual confusions](https://discourse.julialang.org/t/julia-type-system-manual-confusions/132583).

---

<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:** [December 13, 2025, 4:59pm UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/7 "2025-12-13T16:59:59Z")

</div>

> [@sgaure](#):
>
> whereas an abstract type is merely an organizational element in the type universe, possibly with subtypes (which can be either concrete or abstract).

Or _neither_ (it is not a partition). Consider

```julia
julia> struct Foo{T}
       x::T
       end

julia> isabstracttype(Foo)
false

julia> isconcretetype(Foo)
false

julia> isconcretetype(Foo{Float64})
true

```

Primitive types are red herring in this discussion, and as other suggested, they should be ignored completely by new users.

Composite types are what C and a lot of other languages call _structures_ (they even have the same name in Julia). The extra wrinkle is that they can get parameters. That’s what makes Julia Julia.

_Concreteness_ means that all of these parameters have been specified. Eg

```julia
julia> struct Bar
       x
       end

julia> isconcretetype(Bar)
true

```

has no parameters, so it is concrete, even though does may not have a self-contained layout in memory.

---

<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:** [December 14, 2025, 1:55am UTC](https://discourse.julialang.org/t/understanding-primitive-and-composite-types/134477/8 "2025-12-14T01:55:54Z")

</div>

> [@Tamas\_Papp](#):
>
> _Concreteness_ means that all of these parameters have been specified.

To expand on this, a type is concrete if it is neither the [bottom](https://docs.julialang.org/en/v1.14-dev/base/base/#Union%7B%7D) type (`typ == Union{}`), a [`Union`](https://docs.julialang.org/en/v1.14-dev/base/base/#Core.Union) type (`typ isa Union`), nor a [`UnionAll`](https://docs.julialang.org/en/v1.14-dev/base/base/#Core.UnionAll) type (`typ isa UnionAll`). That is, only [`DataType`](https://docs.julialang.org/en/v1.14-dev/base/base/#Core.DataType) types (`typ isa DataType`) are concrete. (Technically, this classification is not complete, as a type may also be a `TypeVar`, however that is mostly an implementation detail, at least as far as beginners are concerned.)

Furthermore, subtypes of `Tuple` are a special case, for example: `!isconcretetype(Tuple{Vararg{String}})` even though `Tuple{Vararg{String}} isa DataType`.

For completeness: the actual definition of concreteness is simpler than above, as in Julia’s docs, a type is said to be concrete if it is the return value of some `typeof` call.

See also:

- [`isconcretetype`](https://docs.julialang.org/en/v1.14-dev/base/base/#Base.isconcretetype) doc string

- [`<:`](https://docs.julialang.org/en/v1.14-dev/base/base/#Core.:%3C:) (subtyping) doc string
