Julia: Split module into multiple files - module

I am using Julia v0.6.4. Consider the following module, saved in FooModule.jl:
module FooModule
export Foo
export doSomething
mutable struct Foo
x::Int64
function Foo(xin::Int64)
new(xin)
end # Foo
end # struct
function doSomething(F::Foo)
println(F.x+123)
end # doSomething
end # module
And a main.jl:
using FooModule
function main()
F = Foo(10)
doSomething(F)
end # main
Now I start the Julia REPL:
julia> include("main.jl");
julia> main()
133
I.e. I get what I expect.
Now imagine doSomething contains many lines and I want to split FooModule.jl. I would like to do something like this:
module FooModule
export Foo
export doSomething
include("doSomething.jl") # <- include instead of writing out
mutable struct Foo
x::Int64
function Foo(xin::Int64)
new(xin)
end # Foo
end # struct
end # module
and doSomething.jl
function doSomething(F::Foo)
println(F.x+123)
end # doSomething
This gives an error since doSomething.jl knows nothing about Foo.
julia> include("main.jl");
ERROR: LoadError: LoadError: LoadError: UndefVarError: Foo not defined
Stacktrace:
[1] include_from_node1(::String) at ./loading.jl:576
[2] include(::String) at ./sysimg.jl:14
[3] include_from_node1(::String) at ./loading.jl:576
[4] eval(::Module, ::Any) at ./boot.jl:235
[5] _require(::Symbol) at ./loading.jl:490
[6] require(::Symbol) at ./loading.jl:405
[7] include_from_node1(::String) at ./loading.jl:576
[8] include(::String) at ./sysimg.jl:14
while loading ~/Desktop/doSomething.jl, in expression starting on line 1
while loading ~/Desktop/FooModule.jl, in expression starting on line 6
while loading ~/Desktop/main.jl, in expression starting on line 1
How can I resolve this problem?

Just move the include("doSomething.jl") to where the function was originally, i.e. after the type definition. You can think of include as copy-pasting the code into the file.

Related

Confusion on when to use T.let vs. T.cast

Based on: https://sorbet.org/docs/type-assertions
In my mind, T.cast seems like a more powerful version of T.let. T.cast(expr, type) forces Sorbet to accept expr as type, even if expr cannot be statically verified as type. T.let(expr, type) hints to Sorbet that expr has type, and Sorbet will be able to typecheck that hint is valid. Based on this understanding, I would assume that T.cast could always be used in place of T.let, but T.let is preferred because Sorbet is providing the most type safety. However, I recently found a case where that is not true (see below).
This example is based on: https://sorbet.org/docs/error-reference#7001
# typed: true
class Main
extend T::Sig
sig { params(elem: Integer).returns(T::Boolean) }
def valid?(elem)
elem == 0
end
sig { params(list: T::Array[Integer]).returns(T::Boolean) }
def works(list)
# Declare `all_valid` with type `T::Boolean`
all_valid = T.let(false, T::Boolean)
list.each do |elem|
# Does not change the type of `all_valid`
all_valid &&= valid?(elem) # ok
end
all_valid
end
sig { params(list: T::Array[Integer]).returns(T::Boolean) }
def does_not_work(list)
all_valid = T.cast(false, T::Boolean)
list.each do |elem|
# ERROR! Changing the type of a variable in a loop is not permitted
all_valid &&= valid?(elem)
end
all_valid
end
end
Why does this work with T.let but not with T.cast?
The T.let annotation explicitly widens the type from FalseClass to T:Boolean
T.cast on the other hand simply relaxes Sorbet’s static checks, it doesn’t silence type errors at runtime that occur from treating TrueClass or FalseClass as T::Boolean.

Paste mode / multi-line-snippet in the kotlin REPL?

Given the skeleton of a function:
fun reformat(
str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ',
) {
println("In reformat")
}
I pasted this into the kotlin REPL and it complained - clearly due to not realizing it were being sent a multi-line snippet:
>>> fun reformat(
error: parameter name expected
fun reformat(
^
error: expecting comma or ')'
fun reformat(
^
error: expecting ')'
fun reformat(
^
>>> str: String,
error: expecting an element
str: String,
^
error: expecting an element
str: String,
^
etc ..
What is the equivalent of the :paste in the scala REPL?
It looks like the Kotlin REPL is intended to be used from the IDE. If you open it from within Intellij IDEA, it works like a charm and allows you multiline input. I don't see any options which allow you to do the same from the regular shell. So, in your case, you can either declare a function in one line or load the script from a file.
I found a project - apparently actively improved by the JetBrains kotlin developers themselves that addresses this and other issues: https://github.com/Kotlin/kotlin-interactive-shell . Here is their help
[0] :h
:quit or q quit the shell
:load or l <path> load file and evaluate
:type or t <expr> display the type of an expression without evaluating it
:list or ls list defined symbols
:help or h [command] print this summary or command-specific help
:paste or p enter paste mode
:syntax {on | off} syntax highlighting
:prompt [pattern] customize prompt
:set set configuration parameter
:conf list configuration parameters
:dependsOn <artifact coordinates> Load dependency from the current set of repositories
:repository <repository coordinates> Add repository for the artifacts lookup
:classpath or cp Show current script compilation classpath
Notice in particular :
[1] :p
// Entering paste mode (ctrl-D to finish)
That is precisely what I am looking for. It does work (though with ugly JDK9 warnings):
[1] :p
// Entering paste mode (ctrl-D to finish)
fun reformat(
str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ',
) {
println("In reformat")
}
// Exiting paste mode, now interpreting.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.jetbrains.kotlin.com.intellij.util.ReflectionUtil (file:/Users/steve/git/kotlin-interactive-shell/ki-shell/target/ki-shell-0.4-SNAPSHOT-shaded.jar) to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of org.jetbrains.kotlin.com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[2] reformat()
ERROR No value passed for parameter 'str' (Line_3.kts:1:10)
[3] reformat("abc")
In reformat

How do I take a reference to new?

Suppose I have the following code:
my constant #suits = <Clubs Hearts Spades Diamonds>;
my constant #values = 2..14;
class Card {
has $.suit;
has $.value;
# order is mnemonic of "$value of $suit", i.e. "3 of Clubs"
multi method new($value, $suit) {
return self.bless(:$suit, :$value);
}
}
It defines some suits and some values and what it means to be a card.
Now, to build a deck, I essentially need to take the cross product of the suits and the values and apply that to the constructor.
The naiive approach to do this, would of course be to just iterate with a loop:
my #deck = gather for #values X #suits -> ($v, $c) {
take Card.new($v, $c);
}
But this is Raku, we have a cross function that can take a function as an optional argument!, so of course I'm gonna do that!
my #deck = cross(#values, #suits, :with(Card.new));
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
... wait no.
What about this?
my #deck = cross(#values, #suits):with(Card.new);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
Still nothing. Reference maybe?
my #deck = cross(#values, #suits):with(&Card.new);
# ===SORRY!=== Error while compiling D:\Code\Raku/.\example.raku
# Illegally post-declared type:
# Card used at line 36
I read somewhere I can turn a function into an infix operator with []
my #deck = cross(#values, #suits):with([Card.new]);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
That also doesn't work.
If classes are supposed to just be modules, shouldn't I then be able to pass a function reference?
Also why is it saying 'with' is that's unexpected? If I'm intuiting this right, what it's actually complaining about is the type of the input, rather than the named argument.
The error message is indeed confusing.
The :with parameter expects a Callable. Card.new is not a Callable. If you write it as :with( { Card.new($^number, $^suit) } ), it appears to work.
Note that I did not use $^value, $^suit, because they order differently alphabetically, so would produce the values in the wrong order. See The ^ twigil for more information on that syntax.
The error is LTA, this makes it a little bit better.
To get back to your question: you can find the code object that corresponds to Card.new with ^find_method. However, that will not work, as Card.new actually expects 3 arguments: the invocant (aka self), $value and $suit. Whereas the cross function will only pass the value and the suit.
The title of your question is “How do I take a reference to new?”, but that is not really what you want to do.
Raku being Raku, you can actually get a reference to new.
my $ref = Card.^lookup('new');
You can't use it like you want to though.
$ref(2,'Clubs'); # ERROR
The problem is that methods take a class or instance as the first argument.
$ref(Card, 2,'Clubs');
You could use .assuming to add it in.
$ref .= assuming(Card);
$ref(2,'Clubs');
But that isn't really any better than creating a block lambda
$ref = { Card.new( |#_ ) }
$ref(2,'Clubs');
All of these work:
cross( #values, #suits ) :with({Card.new(|#_)}) # adverb outside
cross( #values, #suits, :with({Card.new(|#_)}) ) # inside at end
cross( :with({Card.new(|#_)}), #values, #suits ) # inside at beginning
#values X[&( {Card.new(|#_)} )] #suits # cross meta-op with fake infix op
do {
sub new-card ($value,$suit) { Card.new(:$value,:$suit) }
#values X[&new-card] #suits
}
do {
sub with ($value,$suit) { Card.new(:$value,:$suit) }
cross(#values,#suits):&with
}

Generating a method for each subtype within a module

(Reposted from Julia slack for posterity)
let’s say I have some constants like
const FooConst = 1
const BarConst = 2
and I also have some struct
struct Foo end
struct Bar end
and I now want to define a method for each struct to look up that constant
f(::Type{Foo}) = FooConst
f(::Type{Bar}) = BarConst
how would I achieve that last block using metaprogramming? I'm essentially trying to tack on Const to the end of the name of the Struct and look that up in the code
...
...(this) works outside of a module, but in my module the constants aren't exported. After I import my module, f isn't able to look the constants up. MWE here:
module M
import InteractiveUtils: subtypes
export Foo, Bar, f
abstract type Super end
struct Foo <: Super end
struct Bar <: Super end
const FooConst = 1
const BarConst = 2
for T in subtypes(Super)
#eval f(::Type{$T}) = $(Symbol(T, "Const"))
end
end # module
and then in my REPL:
julia> using Main.M
julia> f(Foo)
ERROR: UndefVarError: Main.M.FooConst not defined
Stacktrace:
[1] f(::Type{Foo}) at ./none:11
[2] top-level scope at none:0
I am however able to access it directly:
julia> Main.M.FooConst
1
From Mason Protter on Julia slack:
Mason Protter 2:26 PM
#Sebastian Rollen The issue was the Symbol(T, "const"). That actually ended up expanding to Symbol("Main.Foo.FooConst") and Symbol("Main.Foo.BarConst") instead of Symbol("FooConst") and Symbol("BarConst") respectively. You can fix that using Symbol(nameof(T), "Const") as shown here:
module M
import InteractiveUtils: subtypes
export Foo, Bar, f
abstract type Super end
struct Foo <: Super end
struct Bar <: Super end
const FooConst = 1
const BarConst = 2
for T in subtypes(Super)
#eval f(::Type{$T}) = $(Symbol(nameof(T), "Const"))
end
end # module
julia> using .M; f(Foo)
1
julia> f(Bar)
2
Make sure you restart Julia before running this code or Julia will continue to use the exported functions from the old version of your module.

OCAML first class modules signature inference

1) Suppose there is a module
module Int_Sig_1 =
struct
let x = 1
end
;;
2) and an explicit signature of this module
module type INT_SIG =
sig
val x:int
end
;;
3) and i create a first-class module based on the above module and module type
let int_sig_1 = (module Int_Sig_1:INT_SIG)
4) Now i create another module with no explicit signature but same inferred signature as above
module Int_Sig_2 =
struct
let x =2
end
;;
5) As written in Chapter 10 of the book Real World OCAML "The module type doesn't need to be part of the construction of a first-class module if it can be inferred", i try to create a second first class module using the above module but with no explicit module type
let a2 = (module Int_Sig_2);;
I get the following error
Error: The signature for this packaged module couldn't be inferred.
6) then i try to do the same as above in 5, but this time I put the first class module with no module type being create as an element of a list, where the head of the list is a first class module that was created out of an explicit signature in 3 above.
let int_sig= [int_sig_1;(module Int_Sig_2)];;
val int_sig : (module INT_SIG) list = [<module>; <module>] ;;
My question is why 5 above gives me an error and 6 does not fail ?
The problem with (5) is that in general, there are multiple module types that could be infered. In your example, there is at least two valid module types which could be used to pack Int_Sig_2:
module type empty = sig end
module type with_x = sig val x:int end
In other words, both
let a2 = (module Int_Sig_2: empty)
let a2_bis = (module Int_Sig_2:with_x)
are valid. Consequently, the type checker will not try to infer a module type in this situation.
Contrarily, in your example (6), the type of the list is determined by its first element, whose type is (module INT_SIG_2), therefore the type-checker can use this information to infer that the expected type for the second element of the list is (module INT_SIG_2). Nevertheless, reversing the two elements yields a type error. In other words, this is fine:
[(module struct let x = 2 end: with_x); (module struct let x = 1 end)]
however, the reverse yields
[(module struct let x=2 end); (module struct let x = 3 end:with_x)];;
Error: The signature for this packaged module couldn't be inferred.
This is due to the fact that the type-checker has a left-to-right bias and types first the first element of the list first.