Conversions

One of the main ideas of GAP.jl is that automatic conversions of Julia objects to GAP objects and vice versa shall be avoided whenever this is possible. For a few types of objects, such conversions are unavoidable, see Automatic GAP-to-Julia and Julia-to-GAP Conversions.

In all other situations, the user must explicitly convert between GAP objects and corresponding Julia objects. This is typically done by "type coercion", also just called "coercion": to convert a Julia object x into a GAP object, you may write GapObj(x), see GapObj. Conversely, if y is a GAP object, then e.g. Vector{Int}(y) will attempt to convert it into a Vector{Int}. This will success if e.g. y is a GAP range or a plain list of integers. See also Constructor Methods for GAP-to-Julia Conversions.

For interactive use it may also be convenient to use the function gap_to_julia with a single argument, which will attempt to "guess" a suitable Julia type for the conversion (e.g. GAP strings will be converted to Julia strings). However, we generally recommend against using it, as usually it is better to coerce to a specific type, as that makes it easier to reason about the code, and helps code to become "type stable" (an important concept for writing performant Julia code).

Automatic GAP-to-Julia and Julia-to-GAP Conversions

When one calls a GAP function with Julia objects as arguments, or a Julia function with GAP objects as arguments, the arguments are in general not automatically converted to GAP objects or Julia objects, respectively. The exceptions are as follows.

  • GAP's immediate integers (in the range -2^60 to 2^60-1) are automatically converted to Julia's Int64 objects; Julia's Int64 objects are automatically converted to GAP's immediate integers if they fit, and to GAP's large integers otherwise.

  • GAP's immediate finite field elements are automatically converted to Julia's GAP.FFE objects, and vice versa.

  • GAP's true and false are automatically converted to Julia's true and false, and vice versa.

Explicit GAP-to-Julia and Julia-to-GAP Conversions

The following rules hold for explicit conversions.

  1. Julia types control the conversions.

    • For conversions from Julia to GAP, there is at most one possibility on the GAP side, and the type of the given Julia object determines which code is used.

    • For conversions from GAP to Julia, several Julia types can be possible for the result, for example a GAP integer can be converted to several Julia integer types, or a GAP string can be converted to a Julia String or Tuple. Usually one wants to specify the target type, and then this type determines which code is used for the conversion. If one does not specify the target type, a default type will be chosen.

      Certain Julia types are not supported, for technical reasons. For example, converting a nonempty GAP list to a Julia object of type Set{GapObj} is not possible because no hash method is defined for GapObj.

  2. Subobjects, recursive conversions

    • GAP lists and records can have subobjects, the same holds for various Julia objects such as vectors, matrices, tuples, and dictionaries. One may or may not want to convert the subobjects recursively, this is controlled by the recursive keyword argument of the functions GAP.gap_to_julia and GapObj, which can be set to true or false.

    • For both GAP-to-Julia and Julia-to-GAP conversion, the default is non-recursive conversion.

    • For Julia-to-GAP conversion, recursion stops at subobjects of type GapObj.

    • For GAP-to-Julia conversion, recursion stops at subobjects that do not have the type GAP.Obj.

    • For GAP-to-Julia conversion, the given target type may force a conversion of subobjects up to a certain level also if non-recursive conversion is requested. In this case, recursive conversion means to convert subobjects to Julia also if the result has already the target type.

      For example, converting a GAP list of lists l to a Julia object of type Vector{Vector{Any}} means to convert the entries of l to Vector{Any} objects, and non-recursive conversion means that the entries of the l[i] will be kept in the result since the type requirement Any is satisfied, whereas these entries will get converted to Julia objects in the case of recursive conversion.

    • When recursive conversion is requested, identical subobjects in the given object correspond to identical subobjects in the result of the conversion.

      In order to achieve this, a dictionary gets created in the case of recursive conversion, which stores the subobjects and their conversion results. Some of the implications are as follows.

      • Recursive conversion is more expensive than non-recursive conversion.

      • It can happen that the results of recursive and non-recursive conversion are equal, but they differ w.r.t. the identity of subobjects. For example, the two entries of the GAP list GAP.evalstr("[ [ 1, 2 ], ~[1] ]") are identical, the same holds for the two entries of the vector obtained by recursive conversion of this list to an object of type Vector{Vector{Int}}; however, the two entries of the vector obtained by non-recursive conversion of this list to an object of type Vector{Vector{Int}} are equal but not identical.

      (Note that "identity of objects" has different meanings in GAP and Julia. For example, converting a GAP list of equal but nonidentical strings to a Julia vector of symbols will yield an object with identical subobjects.)

  3. Mutability of results of conversions

    • In GAP, mutability is defined for individual objects. GAP objects that are newly created by Julia-to-GAP conversions are mutable whenever this is possible.

    • In Julia, mutability is defined for types. (The type GapObj is a mutable type.)

  4. Implementation of conversion methods

    • In order to install a new GAP-to-Julia conversion for some prescribed target type T, one has to install a GAP.gap_to_julia_internal method where T is specified as the first argument.

    • In order to install a new Julia-to-GAP conversion for objects of type T, one has to install a GAP.GapObj_internal method. If one knows that objects of type T need not support recursive conversion then one can alternatively use the GAP.@install macro for the installation.

GAP.gap_to_juliaFunction
gap_to_julia([type, ]x; recursive::Bool=false)

Try to convert the object x to a Julia object of type type. If x is a GapObj then the conversion rules are defined in the manual of the GAP package JuliaInterface. If x is another GAP.Obj (for example a Int64) then the result is defined in Julia by type.

For GAP lists and records, it makes sense to either convert also the subobjects recursively, or to keep the subobjects as they are; the behaviour is controlled by recursive, which can be true or false.

Examples

julia> GAP.gap_to_julia(GapObj(1//3))
1//3

julia> GAP.gap_to_julia(GapObj("abc"))
"abc"

julia> val = GapObj([1 2 ; 3 4])
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> GAP.gap_to_julia(val, recursive = true)
2-element Vector{Any}:
 Any[1, 2]
 Any[3, 4]

julia> GAP.gap_to_julia(val)
2-element Vector{Any}:
 GAP: [ 1, 2 ]
 GAP: [ 3, 4 ]

julia> GAP.gap_to_julia(Vector{GapObj}, val)
2-element Vector{GapObj}:
 GAP: [ 1, 2 ]
 GAP: [ 3, 4 ]

julia> GAP.gap_to_julia(Matrix{Int}, val)
2×2 Matrix{Int64}:
 1  2
 3  4

The following gap_to_julia conversions are supported by GAP.jl. (Other Julia packages may provide conversions for more GAP objects.)

GAP filterdefault Julia typeother Julia types
IsIntBigIntT <: Integer
IsFFEFFE
IsBoolBool
IsRatRational{BigInt}Rational{T}
IsFloatFloat64T <: AbstractFloat
IsCharCucharChar
IsStringRepStringSymbol, Vector{T}
IsRangeRepStepRange{Int64,Int64}Vector{T}
IsBListRepBitVectorVector{T}
IsListVector{Any}Vector{T}
IsMatrixObjMatrix{Any}Matrix{T}
IsVectorObjVector{Any}Vector{T}
IsRecordDict{Symbol, Any}Dict{Symbol, T}
source
GAP.GapObjMethod
GapObj(input, recursive::Bool = false)

One can use the type GapObj as a constructor, in order to convert the julia object input to an appropriate GAP object.

If recursive is set to true, recursive conversion of nested Julia objects (arrays, tuples, and dictionaries) is performed.

Examples

julia> GapObj(1//3)
GAP: 1/3

julia> GapObj("abc")
GAP: "abc"

julia> GapObj([1 2; 3 4])
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> GapObj([[1, 2], [3, 4]])
GAP: [ <Julia: [1, 2]>, <Julia: [3, 4]> ]

julia> GapObj([[1, 2], [3, 4]], true)
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> GapObj([[1, 2], [3, 4]], recursive = true)
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

Note that this conversion is not restricted to outputs that actually are of type GapObj, also GAP integers, finite field elements, and booleans can be created by the constructor GapObj.

julia> res = GapObj(42);  res isa GapObj
false

julia> res isa GAP.Obj
true

The following GapObj conversions are supported by GAP.jl. (Other Julia packages may provide conversions for more Julia objects.)

Julia typeGAP filter
Int8, Int16, ..., BigIntIsInt
FFEIsFFE
BoolIsBool
Rational{T}IsRat
Float16, Float32, Float64IsFloat
AbstractStringIsString
SymbolIsString
CharIsChar
Vector{T}IsList
Vector{Bool}, BitVectorIsBList
Set{T}IsList
Tuple{T}IsList
Matrix{T}IsList
Dict{String, T}, Dict{Symbol, T}IsRecord
UnitRange{T}, StepRange{T, S}IsRange
FunctionIsFunction
source

Constructor Methods for GAP-to-Julia Conversions

(For Julia-to-GAP conversions, one can use GapObj and GAP.Obj as constructors.)

Core.Int128Type
Int128(obj::GapObj)

Return the Int128 converted from the GAP integer obj. (Note that small GAP integers are represented by Julia Int64 objects, in particular they are not GapObjs; their conversion is not handled by methods installed in GAP.jl.)

Examples

julia> val = GAP.Globals.Factorial(25)
GAP: 15511210043330985984000000

julia> Int128(val)
15511210043330985984000000

julia> Int(val)
ERROR: InexactError: Int64(15511210043330985984000000)
source
Base.GMP.BigIntType
BigInt(obj::GapObj)

Return the big integer converted from the GAP integer obj. (Note that small GAP integers are not represented by GapObjs, their conversion with BigInt is handled by Julia's methods.)

Examples

julia> val = GAP.Globals.Factorial(25)
GAP: 15511210043330985984000000

julia> BigInt(val)
15511210043330985984000000

julia> val = GAP.Globals.Factorial(10)
3628800

julia> isa(val, GapObj)
false

julia> BigInt(val)
3628800
source
Base.RationalType
Rational{T}(obj::GapObj) where {T<:Integer}

Return the rational converted from the GAP integer or the GAP rational obj,

Examples

julia> val = GAP.Globals.Factorial(25)
GAP: 15511210043330985984000000

julia> Rational{Int128}(val)
15511210043330985984000000//1

julia> Rational{BigInt}(val)
15511210043330985984000000//1

julia> val = GAP.Obj(1//3)
GAP: 1/3

julia> Rational{Int64}(val)
1//3
source
Core.Float64Type
Float64(obj::GapObj)

Return the float converted from the GAP float obj.

Examples

julia> val = GAP.Obj(2.2)
GAP: 2.2

julia> Float64(val)
2.2

julia> Float32(val)
2.2f0
source
Core.CharType
Char(obj::GapObj)

Return the character converted from the GAP character obj.

Examples

julia> val = GAP.Obj('x')
GAP: 'x'

julia> Char(val)
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)
source
Base.CucharType
Cuchar(obj::GapObj)

Return the UInt8 that belongs to the GAP character obj.

Examples

julia> val = GAP.Obj('x')
GAP: 'x'

julia> Cuchar(val)
0x78
source
Core.StringType
String(obj::GapObj)

Return the Julia string converted from the GAP string obj. Note that GAP's String function can be applied to arbitrary GAP objects, similar to Julia's string function; this behaviour is not intended for this String constructor.

Examples

julia> val = GAP.Obj("abc")
GAP: "abc"

julia> String(val)
"abc"

julia> val = GAP.Obj([])
GAP: [  ]

julia> String(val)   # an empty GAP list is a string
""
source
Core.SymbolType
Symbol(obj::GapObj)

Return the symbol converted from the GAP string obj.

Examples

julia> str = GAP.Obj("abc")
GAP: "abc"

julia> Symbol(str)
:abc
source
Base.UnitRangeType
UnitRange(obj::GapObj)

Return the unit range converted from the GAP range obj, which has step width 1.

Examples

julia> val = GAP.Obj(1:10)
GAP: [ 1 .. 10 ]

julia> UnitRange(val)
1:10

julia> UnitRange{Int32}(val)
1:10
source
Base.StepRangeType
StepRange(obj::GapObj)

Return the step range converted from the GAP range obj, which may have arbitrary step width.

Examples

julia> val = GAP.Obj(1:2:11)
GAP: [ 1, 3 .. 11 ]

julia> StepRange(val)
1:2:11

julia> r = StepRange{Int8,Int8}(val)
1:2:11

julia> typeof(r)
StepRange{Int8, Int8}
source
Core.TupleType
Tuple{Types...}(obj::GapObj; recursive::Bool = false)

Return the tuple converted from the GAP list obj. The entries of the list are converted to the required types Types.... If recursive is true then the entries of the list are converted recursively, otherwise non-recursively.

Examples

julia> val = GAP.Obj([1, 5])
GAP: [ 1, 5 ]

julia> Tuple{Int64,Int64}(val)
(1, 5)

julia> val = GAP.Obj([[1], [2]]; recursive=true)
GAP: [ [ 1 ], [ 2 ] ]

julia> Tuple{Any,Any}(val; recursive=true)
(Any[1], Any[2])

julia> Tuple{GapObj,GapObj}(val)
(GAP: [ 1 ], GAP: [ 2 ])
source
Base.BitVectorType
BitVector(obj::GapObj)

Return the bit vector converted from the GAP list of booleans obj.

Examples

julia> val = GAP.Obj([true, false, true])
GAP: [ true, false, true ]

julia> BitVector(val)
3-element BitVector:
 1
 0
 1
source
Base.VectorType
Vector{T}(obj::GapObj; recursive::Bool = false)

Return the 1-dimensional array converted from the GAP list obj. The entries of the list are converted to the type T. If recursive is true then the entries of the list are converted recursively, otherwise non-recursively.

If T is UInt8 then obj may be a GAP string.

Examples

julia> val = GAP.Obj([[1], [2]]; recursive=true)
GAP: [ [ 1 ], [ 2 ] ]

julia> Vector{Any}(val; recursive=true)
2-element Vector{Any}:
 Any[1]
 Any[2]

julia> Vector{Any}(val)
2-element Vector{Any}:
 GAP: [ 1 ]
 GAP: [ 2 ]

julia> Vector{Vector{Int64}}(val)
2-element Vector{Vector{Int64}}:
 [1]
 [2]

julia> val = GAP.evalstr( "NewVector( IsPlistVectorRep, Integers, [ 0, 2, 5 ] )" )
GAP: <plist vector over Integers of length 3>

julia> Vector{Int64}(val)
3-element Vector{Int64}:
 0
 2
 5

julia> val = GAP.Obj("abc")
GAP: "abc"

julia> Vector{UInt8}(val)
3-element Vector{UInt8}:
 0x61
 0x62
 0x63
source
Base.MatrixType
Matrix{T}(obj::GapObj; recursive::Bool = false)

Return the 2-dimensional array converted from the GAP matrix obj, which can be a GAP list of lists or a GAP matrix object. The entries of the matrix are converted to the type T. If recursive is true then the entries are converted recursively, otherwise non-recursively.

Examples

julia> val = GAP.Obj([[1, 2], [3, 4]]; recursive=true)
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> Matrix{Int64}(val)
2×2 Matrix{Int64}:
 1  2
 3  4

julia> val = GAP.evalstr( "NewMatrix( IsPlistMatrixRep, Integers, 2, [ 0, 1, 2, 3 ] )" )
GAP: <2x2-matrix over Integers>

julia> Matrix{Int64}(val)
2×2 Matrix{Int64}:
 0  1
 2  3
source
Base.SetType
Set{T}(obj::GapObj; recursive::Bool = false)

Return the set converted from the GAP list or GAP collection obj. The elements of obj are converted to the required type T. If recursive is true then the elements are converted recursively, otherwise non-recursively.

This constructor method is intended for situations where the result involves only native Julia objects such as integers and strings. Dealing with results containing GAP objects will be inefficient.

Examples

julia> Set{Int}(GAP.Obj([1, 2, 1]))
Set{Int64} with 2 elements:
  2
  1

julia> Set{Vector{Int}}(GAP.Obj([[1], [2], [1]]))
Set{Vector{Int64}} with 2 elements:
  [1]
  [2]

julia> Set{String}(GAP.Obj(["a", "b"]; recursive=true))
Set{String} with 2 elements:
  "b"
  "a"

julia> Set{Any}(GAP.Obj([[1], [2], [1]]; recursive=true))
Set{Any} with 2 elements:
  Any[1]
  Any[2]
source
Base.DictType
Dict{Symbol,T}(obj::GapObj; recursive::Bool = false)

Return the dictionary converted from the GAP record obj. If recursive is true then the values of the record components are recursively converted to objects of the type T.

Examples

julia> val = GAP.Obj(Dict(:a => 1, :b => 2))
GAP: rec( a := 1, b := 2 )

julia> Dict{Symbol,Int}(val)
Dict{Symbol, Int64} with 2 entries:
  :a => 1
  :b => 2

julia> val = GAP.Obj(Dict(:l => GAP.Obj([1, 2])))
GAP: rec( l := [ 1, 2 ] )

julia> Dict{Symbol,Any}(val)
Dict{Symbol, Any} with 1 entry:
  :l => GAP: [ 1, 2 ]

julia> Dict{Symbol,Any}(val; recursive=true)
Dict{Symbol, Any} with 1 entry:
  :l => Any[1, 2]

julia> Dict{Symbol,Vector{Int}}(val; recursive=true)
Dict{Symbol, Vector{Int64}} with 1 entry:
  :l => [1, 2]
source