Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop wrapping Julia modules in special objects #1049

Merged
merged 1 commit into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 module Main.foo>
gap> Julia.foo.x;
1
gap> JuliaEvalString("module foo x = 2 end");
<Julia module Main.foo>
gap> Julia.foo.x;
2

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