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

Feature: nicer input syntax for permutations #420

Closed
fingolfin opened this issue Apr 16, 2020 · 5 comments · Fixed by oscar-system/Oscar.jl#1307
Closed

Feature: nicer input syntax for permutations #420

fingolfin opened this issue Apr 16, 2020 · 5 comments · Fixed by oscar-system/Oscar.jl#1307
Labels
kind: enhancement New feature or request
Milestone

Comments

@fingolfin
Copy link
Member

fingolfin commented Apr 16, 2020

This is an older idea but I am not sure I ever wrote it down, so here goes: it might be kinda nice to add a macro that makes it convenient to enter permutations in cycle notation, GAP style. This can be achieved using macros. With the MacroTools.jl package, it is particularly convenient:

julia> macro perm(ex)
         MacroTools.postwalk(ex) do x
           @capture(x, f_(xs__)) || return x
           :($f, ($(xs...),))
         end
       end
@perm (macro with 1 method)

julia> @perm (1,2)(3,4,5)(7,8,9)
(((1, 2), (3, 4, 5)), (7, 8, 9))

This works because Julia parses the expression (1,2)(3,4,5) as a function call, where (1,2) is the function and (3,4,5) are the arguments.

With some tweaking, it should be possible to tweak this code to produce a GAP permutation. The easiest approach of course would be to convert those tuples into GAP permutations and then multiply them, but that's (a) not very efficient and (b) does not reject the case where the cycles overlap.

UPDATE: to clarify, this shouldn't just work for integers, but also nested expressions, e.g. this: a=1;b=2; pi = @perm (a,f(a),b)(a+1,b*2) (to give a rather arbitrary example)

@fingolfin fingolfin added the kind: enhancement New feature or request label Apr 16, 2020
@rfourquet
Copy link
Contributor

I don't know whether this has been discussed, but would it be sensible to have this feature within the @gap macro itself? Currently @gap seems brittle (by relying on calling string on an Expr), and for example I would expect (without reading the docstring) @gap "(1, 2)" to return a string (because typing "(1, 2)" in the GAP REPL gives a string, not a permutation).

It was mentioned that

[the @gap macro] may be used for some conveniences, but should not really be used in any functions or so.

What about making @gap more regular by explicitly/manually parsing its input, and having a gap_str string macro to express "parse this string as a GAP expression" ? Then @gap (1, 2)(3, 4) and gap"(1, 2)(3, 4)" would be equivalent, but other currently unsupported features could be added, e.g. @gap x := 1.

@fingolfin
Copy link
Member Author

Wouldn't that require re-implementing the complete GAP parser in Julia, though? Not exactly a project for an afternoon.

@rfourquet
Copy link
Contributor

Yeah, I must admit that I didn't/couldn't evaluate what it would take; I'm just aware of the examples from the @gap docstring or its tests, for which it seems the feature would be relatively easy to implement (function call syntax, array literals).
The general case would be handled by a gap"" string macro, which would always work, and @gap would handle few syntactic constructions which exist in both Julia and GAP.

Anyway, this was just a suggestion after seeing @gap "gap code" used in cases which are idiomatically handled with string macros. I'm fine to give it a try or to forget about it.

@fingolfin
Copy link
Member Author

So originally, @gap was always meant to only work on strings. The fact that it actually works remarkably well for non-strings is only because Julia and GAP happen to have a rather similar syntax, at least on a superficial level.

But yeah, the fact that it sometimes works and sometimes not is an annoying and confusing thing.

We could try to fix it, perhaps it is is not as bad as I thought. Or perhaps we should remove it, and just tell people to use GAP.evalstr as that at least is uniform and predicable in its behaviour..

Some GAP expressions not handled well right now:

julia> macro foo(str) string(str) end
@foo (macro with 1 method)

julia> @foo (1,2)(3,4)
"((1, 2))(3, 4)"

julia> @foo x->x+1
"x->begin\n        #= REPL[33]:1 =#\n        x + 1\n    end"

It seems integer subscripts may not work or require major trickery

julia> @foo G.1
ERROR: LoadError: MethodError: no method matching @foo(::LineNumberNode, ::Module, ::Symbol, ::Float64)
Closest candidates are:
  @foo(::LineNumberNode, ::Module, ::Any) at REPL[30]:1
in expression starting at REPL[45]:1

julia> @foo G.1 * G.2
ERROR: LoadError: MethodError: no method matching @foo(::LineNumberNode, ::Module, ::Symbol, ::Expr, ::Float64)
Closest candidates are:
  @foo(::LineNumberNode, ::Module, ::Any) at REPL[30]:1
in expression starting at REPL[46]:1

I don't think we have a chance to handle non-trivial GAP statements this way, so I wouldn't even bother. But that considerably reduces the scope of the problem.

The question remains whether this is worth spending any efforts on...

@fingolfin
Copy link
Member Author

Here is a simple example to converting a single "syntactic" cycle into an actual Oscar permutation (cperm is an Oscar command)

julia> expr =  :(   (1,2,3) )
:((1, 2, 3))

julia> return :(  cperm( [ $(expr.args...) ]  )   )
:(cperm([1, 2, 3]))

julia> eval(ans)
(1,2,3)

You can inspect expressions using dump:

julia> expr =  :(   (1,2,3) );

julia> dump(expr)
Expr
  head: Symbol tuple
  args: Array{Any}((3,))
    1: Int64 1
    2: Int64 2
    3: Int64 3

julia> expr.head
:tuple

julia> expr.args
3-element Vector{Any}:
 1
 2
 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants