Skip to content

Commit

Permalink
Stop wrapping Julia modules in special objects
Browse files Browse the repository at this point in the history
This resolves issues with outdated caches in our module wrappers.
  • Loading branch information
fingolfin committed Sep 23, 2024
1 parent e60382d commit b350981
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 62 deletions.
29 changes: 4 additions & 25 deletions pkg/JuliaInterface/gap/JuliaInterface.gd
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@
#! However not every object living on the Julia side is in this filter.
#! For example Julia booleans and small <C>Int</C> values are directly
#! translated to GAP booleans and small integers, while for Julia functions
#! and wrappers dedicated wrappers are used for improved efficiency
#! respectively additional features.
#! dedicated wrappers are used to improve efficiency and add features.
#! @BeginExampleSession
#! gap> JuliaEvalString( "x = 4" );;
#! gap> Julia.x;
Expand All @@ -119,8 +118,6 @@
#! false
#! gap> IsJuliaObject( Julia.sqrt );
#! false
#! gap> IsJuliaObject( Julia.Main );
#! false
#! @EndExampleSession
DeclareCategory( "IsJuliaObject", IsObject );

Expand All @@ -138,33 +135,17 @@ BindGlobal("TheTypeJuliaObject", NewType( JuliaObjectFamily, IsJuliaObject ));
#! This admits implementing high-level wrapper objects
#! for &Julia; objects that behave just like the &Julia; objects
#! when used as arguments in calls to &Julia; functions.
#! <!-- No other functionality is implemented for IsJuliaWrapper -->
#!
#! Objects in <Ref Filt="IsJuliaWrapper" Label="for IsObject"/>
#! should <E>not</E> be in the filter
#! <Ref Filt="IsJuliaObject" Label="for IsObject"/>.
#!
#! For example, any Julia modules such as <C>Julia.Base</C> are
#! in the filter <Ref Filt="IsJuliaWrapper" Label="for IsObject"/>.
DeclareCategory( "IsJuliaWrapper", IsObject );

#! @Arguments obj
#! @Description
#! is an attribute for &GAP; objects in the filter
#! <Ref Filt="IsJuliaWrapper" Label="for IsObject"/>.
#! The value must be a &Julia; object.
#! @BeginExampleSession
#! gap> Julia;
#! <Julia module Main>
#! gap> IsJuliaObject( Julia );
#! false
#! gap> IsJuliaWrapper( Julia );
#! true
#! gap> ptr:= JuliaPointer( Julia );
#! <Julia: Main>
#! gap> IsJuliaObject( ptr );
#! true
#! @EndExampleSession
DeclareAttribute( "JuliaPointer", IsJuliaWrapper );

#! @Arguments obj
Expand All @@ -181,10 +162,10 @@ DeclareAttribute( "JuliaPointer", IsJuliaWrapper );
#! gap> Julia.GAP.julia_to_gap;
#! <Julia: julia_to_gap>
#! @EndExampleSession
DeclareCategory( "IsJuliaModule", IsJuliaWrapper and IsRecord );
DeclareCategory( "IsJuliaModule", IsJuliaObject and IsRecord );

BindGlobal( "TheFamilyOfJuliaModules", NewFamily( "TheFamilyOfJuliaModules" ) );
BindGlobal( "TheTypeOfJuliaModules", NewType( TheFamilyOfJuliaModules, IsJuliaModule and IsAttributeStoringRep ) );
BindGlobal( "TheTypeOfJuliaModules", NewType( TheFamilyOfJuliaModules, IsJuliaModule and HasName ) );

#! @Section Creating &Julia; objects

Expand Down Expand Up @@ -271,9 +252,7 @@ DeclareGlobalVariable( "Julia" );
#! @BeginExampleSession
#! gap> JuliaTypeInfo( Julia.GAP );
#! "Module"
#! gap> JuliaTypeInfo( JuliaPointer( Julia.GAP ) );
#! "Module"
#! gap> JuliaTypeInfo( JuliaEvalString( "sqrt(2)" ) );
#! gap> JuliaTypeInfo( Julia.sqrt(2) );
#! "Float64"
#! gap> JuliaTypeInfo( 1 );
#! "Int64"
Expand Down
66 changes: 36 additions & 30 deletions pkg/JuliaInterface/gap/JuliaInterface.gi
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,9 @@
# Implementations
#

BindGlobal( "_JULIA_MODULE_TYPE", _JuliaGetGlobalVariableByModule( "Module", "Core" ) );
BindGlobal( "_JULIA_FUNCTION_TYPE", _JuliaGetGlobalVariableByModule( "Function", "Core" ) );
BindGlobal( "_JULIA_ISA", _WrapJuliaFunction( _JuliaGetGlobalVariableByModule( "isa", "Core" ) ) );

BindGlobal( "_WrapJuliaModule",
function( name, julia_pointer )
local str;

str:= Concatenation( "<Julia module ", name, ">" );

return ObjectifyWithAttributes( rec( storage := rec( ) ),
TheTypeOfJuliaModules,
Name, str,
String, str,
JuliaPointer, julia_pointer );
end );
BindGlobal( "_JULIA_GAP", _JuliaGetGapModule() );

InstallMethod( ViewString,
[ "IsFunction and IsInternalRep and HasNameFunction" ],
Expand All @@ -31,59 +18,78 @@ InstallMethod( ViewString,
TryNextMethod();
end );

InstallMethod( ViewString,
[ "IsJuliaModule" ],
function( module )
return Concatenation( "<Julia module ", JuliaToGAP( IsString, Julia.string( module ) ), ">" );
end );

InstallMethod( Name,
[ "IsJuliaModule" ],
function( module )
return Concatenation( "<Julia module ", JuliaToGAP( IsString, Julia.string( module ) ), ">" );
end );

InstallMethod( \.,
[ "IsJuliaModule", "IsPosInt" ],
[ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ],
function( module, rnum )
local rnam, var;

if IsBound\.( module!.storage, rnum ) then
return \.(module!.storage, rnum );
fi;

rnam := NameRNam( rnum );

var := _JuliaGetGlobalVariableByModule( rnam, JuliaPointer( module ) );
if IsIdenticalObj(module, Julia) and rnam = "GAP" then
## Ensure that the Julia module GAP is always accessible as Julia.GAP,
## even while it is still being initialized, and also if it not actually
## exported to the Julia Main module
return _JULIA_GAP;
fi;

var := _JuliaGetGlobalVariableByModule( rnam, module );
if var = fail then
Error( rnam, " is not bound in Julia" );
fi;

if _JULIA_ISA( var, _JULIA_FUNCTION_TYPE ) then
var := _WrapJuliaFunction( var );
elif _JULIA_ISA( var, _JULIA_MODULE_TYPE ) then
var := _WrapJuliaModule( rnam, var );
\.\:\=( module!.storage, rnum, var );
fi;

return var;
end );

InstallMethod( \.\:\=,
[ "IsJuliaModule", "IsPosInt", "IsObject" ],
[ "IsJuliaModule", "IsPosInt and IsSmallIntRep", "IsObject" ],
function( module, rnum, obj )
Julia.GAP._setglobal( module, JuliaSymbol( NameRNam( rnum ) ), obj );
end );

InstallMethod( IsBound\.,
[ "IsJuliaModule", "IsPosInt" ],
[ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ],
function( module, rnum )
if IsBound\.( module!.storage, rnum ) then
local rnam;

rnam := NameRNam( rnum );
if IsIdenticalObj(module, Julia) and rnam = "GAP" then
## Ensure that the Julia module GAP is always accessible as Julia.GAP,
## even while it is still being initialized, and also if it not actually
## exported to the Julia Main module
return true;
fi;
return fail <> _JuliaGetGlobalVariableByModule( NameRNam( rnum ), JuliaPointer( module ) );

return fail <> _JuliaGetGlobalVariableByModule( rnam, module );
end );

InstallMethod( Unbind\.,
[ "IsJuliaModule", "IsPosInt" ],
[ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ],
function( module, rnum )
Error( "cannot unbind Julia variables" );
end );

InstallMethod(RecNames, [ "IsJuliaModule" ],
function( obj )
return JuliaToGAP( IsList, Julia.GAP.get_symbols_in_module( JuliaPointer( obj ) ), true );
return JuliaToGAP( IsList, Julia.GAP.get_symbols_in_module( obj ), true );
end);

InstallValue( Julia, _WrapJuliaModule( "Main", _JuliaGetGlobalVariableByModule( "Main", "Main" ) ) );
InstallValue( Julia, _JuliaGetGlobalVariableByModule( "Main", "Main" ) );

InstallGlobalFunction( "JuliaIncludeFile",
function( filename, module_name... )
Expand Down
5 changes: 0 additions & 5 deletions pkg/JuliaInterface/read.g
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
#
ReadPackage( "JuliaInterface", "gap/JuliaInterface.gi");

## Ensure that the Julia module GAP is always accessible as Julia.GAP,
## even while it is still being initialized, and also if it not actually
## exported to the Julia Main module
Julia!.storage.GAP := _WrapJuliaModule( "GAP", _JuliaGetGapModule() );

ReadPackage( "JuliaInterface", "gap/adapter.gi");
ReadPackage( "JuliaInterface", "gap/calls.gi");
ReadPackage( "JuliaInterface", "gap/convert.gi");
Expand Down
7 changes: 6 additions & 1 deletion pkg/JuliaInterface/src/JuliaInterface.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static jl_datatype_t * JULIA_GAPFFE_type;

static jl_datatype_t * gap_datatype_mptr;

static Obj TheTypeOfJuliaModules;
static Obj TheTypeJuliaObject;
static UInt T_JULIA_OBJ;

Expand Down Expand Up @@ -114,7 +115,10 @@ jl_value_t * GET_JULIA_OBJ(Obj o)

static Obj JuliaObjectTypeFunc(Obj o)
{
return TheTypeJuliaObject;
if (jl_typeis(GET_JULIA_OBJ(o), jl_module_type))
return TheTypeOfJuliaModules;
else
return TheTypeJuliaObject;
}

Obj NewJuliaObj(jl_value_t * v)
Expand Down Expand Up @@ -282,6 +286,7 @@ static Int InitKernel(StructInitInfo * module)
// init filters and functions
InitHdlrFuncsFromTable(GVarFuncs);

InitCopyGVar("TheTypeOfJuliaModules", &TheTypeOfJuliaModules);
InitCopyGVar("TheTypeJuliaObject", &TheTypeJuliaObject);

T_JULIA_OBJ = RegisterPackageTNUM("JuliaObject", JuliaObjectTypeFunc);
Expand Down
12 changes: 11 additions & 1 deletion pkg/JuliaInterface/tst/utils.tst
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ gap> _JuliaGetGlobalVariableByModule("Base","sqrt");
Error, sqrt is not a module
gap> _JuliaGetGlobalVariableByModule("sqrt","Base");
<Julia: sqrt>
gap> _JuliaGetGlobalVariableByModule("sqrt", JuliaPointer(Julia.Base));
gap> _JuliaGetGlobalVariableByModule("sqrt", Julia.Base);
<Julia: sqrt>

##
Expand Down Expand Up @@ -85,5 +85,15 @@ gap> path:= GetJuliaScratchspace( "test_scratch" );;
gap> IsDirectoryPath( path );
true

# Julia modules should not get cached, see #1044
gap> JuliaEvalString("module foo x = 1 end");
<Julia: Main.foo>
gap> Julia.foo.x;
1
gap> JuliaEvalString("module foo x = 2 end");
<Julia: Main.foo>
gap> Julia.foo.x;
2

##
gap> STOP_TEST( "utils.tst" );

0 comments on commit b350981

Please sign in to comment.