MethodError for Composite Types

I would like to create a composite type, initialize some constant objects of that type, and then use the objects with methods I’ve coded. For example, below I have created a folder with 3 files:

  1. RectangleMod.jl - Defines the Rectangle type and a corresponding method to calculate its area:
module RectangleMod

export Rectangle, get_area

struct Rectangle
    width::Number
    height::Number
end

function get_area(rectangle::Rectangle)
    return rectangle.width * rectangle.height;
end

end
  1. savedRectangles.jl - Stores an object of Rectangle type
module savedRectangles
 
include("./RectangleMod.jl")
using .RectangleMod

export MY_RECTANGLE

const MY_RECTANGLE = Rectangle(1.0, 2.0);

end
  1. test_rectangle.jl - Imports MY_RECTANGLE and tries to calculate its area:
include("./savedRectangles.jl")
include("./RectangleMod.jl")

using .savedRectangles
using .RectangleMod

get_area(MY_RECTANGLE)

However, I get the following error when I run test_rectangle.jl:

ERROR: LoadError: MethodError: no method matching get_area(::Main.savedRectangles.RectangleMod.Rectangle)
Closest candidates are:
  get_area(::Rectangle) at c:\Users\jlym\Downloads\test_rectangle\RectangleMod.jl:10
Stacktrace:
 [1] top-level scope
   @ c:\Users\jlym\Downloads\test_rectangle\test_rectangle.jl:7
in expression starting at c:\Users\jlym\Downloads\test_rectangle\test_rectangle.jl:7

The stacktrace suggests MY_RECTANGLE is type ::Main.savedRectangles.RectangleMod.Rectangle, which is not recognized as a ::Rectangle type. What can I do to resolve this?

EDIT: Updated code snippers to enable syntax highlighting

You should use include for a file at most once if at all.

There are larger file organization issues here, but I will try to address this minimally.

Change file 3, test_rectangle.jl, to be this:

include("./savedRectangles.jl")

using .savedRectangles
using .savedRectangles.RectangleMod

get_area(MY_RECTANGLE)

In file 2, you treat RectangleMod as a submodule of savedRectangles, making MY_RECTANGLE a type of savedRectangles.RectangleMod.

It might be better to treat them as separated packages. Try

include("./RectangleMod.jl")

module savedRectangles
using Main.RectangleMod

export MY_RECTANGLE

const MY_RECTANGLE = Rectangle(1.0, 2.0);

end

in file 2.

EDIT: Add prefix Main. to RectangleMod

2 Likes

Thank you! The code ran successfully.

Is this how developers solve the issue when they develop their own packages? It seems cumbersome to have to know how your imported modules import their modules. I’m open to suggestions on restructuring whole code.

Thank you for your suggestion! I moved the include line outside the module but received this error:

ERROR: LoadError: UndefVarError: RectangleMod not defined
Stacktrace:
 [1] include(fname::String)
   @ Base.MainInclude .\client.jl:476
 [2] top-level scope
   @ c:\Users\jlym\Downloads\test_rectangle\test_rectangle.jl:2
in expression starting at c:\Users\jlym\Downloads\test_rectangle\savedRectangles.jl:3

Was there any other changes I should have made?


RectangleMod.jl

module RectangleMod

export Rectangle, get_area

struct Rectangle
    width::Number
    height::Number
end

function get_area(rectangle::Rectangle)
    return rectangle.width * rectangle.height;
end

end

savedRectangles.jl

include("./RectangleMod.jl")

module savedRectangles
using .RectangleMod

export MY_RECTANGLE

const MY_RECTANGLE = Rectangle(1.0, 2.0);

end

test_rectangle.jl

include("./RectangleMod.jl")

include("./savedRectangles.jl")

using .RectangleMod

using .savedRectangles

get_area(MY_RECTANGLE)

Is this how developers solve the issue when they develop their own packages? It seems cumbersome to have to know how your imported modules import their modules. I’m open to suggestions on restructuring whole code.

Mostly we don’t use modules so much - the point for breaking a project into modules is often also the point for breaking them into packages.

The way multiple dispatch works you also have less namespace issues, so you can have more code in a single namespace.

3 Likes

Sorry for the careless. I just edit the post which now get passed.

The module RectangleMod defined in Main should be explicitly called with Main..

This works fine for temporary use, but for stability, I recommend writing as a package (try PkgTemplates), which can be directly called by using xxx anywhere.

1 Like

Thank you all for the help! I agree that creating a package is the best solution. I used PkgTemplates and followed along this tutorial to create the following Rectangles package.

Package File Structure

Rectangles
│   .gitignore
│   LICENSE
│   Manifest.toml
│   Project.toml
│   README.md
│
├───.github
│   └───workflows
│           CI.yml
│           CompatHelper.yml
│           TagBot.yml
│
├───src
│       RectangleMod.jl
│       Rectangles.jl
│
└───test
        runtests.jl

Rectangles/src/RectangleMod.jl

module RectangleMod

export Rectangle, get_area

struct Rectangle
    width::Real
    height::Real
end

function get_area(rectangle::Rectangle)::Real
    return rectangle.width * rectangle.height;
end

end

Rectangles/src/Rectangles.jl

module Rectangles

export Rectangle, get_area

include("./RectangleMod.jl")
using .RectangleMod

end

Rectangles/test/run_tests.jl

using Rectangles
using Test

@testset "Rectangles" begin
    rectangle = Rectangle(1.0, 2.0)

    @test get_area(rectangle) ≈ 1.0*2.0
end

The test passes:

julia> cd("C:\\Users\\jonat\\.julia\\dev\\Rectangles")

julia>

(@v1.8) pkg> activate .
  Activating project at `C:\Users\jonat\.julia\dev\Rectangles`

(Rectangles) pkg> test
     Testing Rectangles
      Status `C:\Users\jonat\AppData\Local\Temp\jl_La3LcN\Project.toml`
  [7f953efd] Rectangles v0.1.0 `C:\Users\jonat\.julia\dev\Rectangles`
  [8dfed614] Test `@stdlib/Test`
      Status `C:\Users\jonat\AppData\Local\Temp\jl_La3LcN\Manifest.toml`
  [7f953efd] Rectangles v0.1.0 `C:\Users\jonat\.julia\dev\Rectangles`
  [2a0f44e3] Base64 `@stdlib/Base64`
  [b77e0a4c] InteractiveUtils `@stdlib/InteractiveUtils`
  [56ddb016] Logging `@stdlib/Logging`
  [d6f4376e] Markdown `@stdlib/Markdown`
  [9a3f8284] Random `@stdlib/Random`
  [ea8e919c] SHA v0.7.0 `@stdlib/SHA`
  [9e88b42a] Serialization `@stdlib/Serialization`
  [8dfed614] Test `@stdlib/Test`
     Testing Running tests...
Test Summary: | Pass  Total  Time
Rectangles    |    1      1  0.1s
     Testing Rectangles tests passed

I can install the package:

(@v1.8) pkg> add "C:\Users\jonat\.julia\dev\Rectangles"
    Updating git-repo `C:\Users\jonat\.julia\dev\Rectangles`
    Updating registry at `C:\Users\jonat\.julia\registries\General.toml`
   Resolving package versions...
    Updating `C:\Users\jonat\.julia\environments\v1.8\Project.toml`
  [7f953efd] + Rectangles v0.1.0 `C:\Users\jonat\.julia\dev\Rectangles#main`
    Updating `C:\Users\jonat\.julia\environments\v1.8\Manifest.toml`
  [7f953efd] + Rectangles v0.1.0 `C:\Users\jonat\.julia\dev\Rectangles#main`

I can import the package:

julia> using Rectangles

But I can’t access anything from it.

julia> Rectangle(1.0, 2.0)
ERROR: UndefVarError: Rectangle not defined
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

Are there any errors standing out? Or would you propose a different way to organize my files?

EDIT: Corrected filename as suggested below.

1 Like

You named the file “RectanglesMod.jl” but you included “RectangleMod.jl” without the ‘s’.

Otherwise, this works fine for me.

julia> using Rectangles

julia> Rectangle(1.0, 2.0)
Rectangle(1.0, 2.0)

Rather than adding the package, you might try using dev:

]dev Rectangles
1 Like

Oh, good catch! It works now! :partying_face:
Thank you everyone again for all the help!

1 Like