Julia: mostrar o código fonte da função de repl

Criado em 20 mar. 2013  ·  22Comentários  ·  Fonte: JuliaLang/julia

Julia tem várias coisas REPL úteis como methods e help .
No entanto, ainda acabo precisando apenas olhar o código para ver o que ele está fazendo.
Seria legal poder fazer:

julia> methods(base)
# methods for generic function base
base(base::Integer,n::Integer,pad::Integer) at intfuncs.jl:290
base(symbols::Array{Uint8,N},n::Integer,p::Integer) at intfuncs.jl:291
base(base_or_symbols::Union(Integer,Array{Uint8,N}),n::Integer) at intfuncs.jl:292

julia> implementation(base,1)
base(base::Integer, n::Integer, pad::Integer) = _base(dig_syms,int(base),unsigned(abs(n)),pad,n<0)

julia> implementation(base,3)
base(base_or_symbols::Union(Integer,Array{Uint8}), n::Integer) = base(base_or_symbols, n, 1)

É como o código de salto em alguns IDEs, mas no REPL, e mostrando apenas uma função.
Isso obviamente tem limitações, já que você não pode simplesmente rolar para cima e ver a implementação de _base ou pesquisar a definição de dig_syms naquele arquivo, mas permite que você veja quais são os valores padrão está.

Você já pode ver a assinatura de _base , o que torna as implementações de base mais significativas. (sem a necessidade de mudar do REPL para um editor de texto)

julia> methods(Base._base)
# methods for generic function _base
_base(symbols::Array{Uint8,N},b::Int32,x::Unsigned,pad::Int32,neg::Bool) at intfuncs.jl:278

Considerando que os números de linha / arquivos já estão incluídos na saída de methods , parece que deve ser simples obter as linhas de código apropriadas do arquivo.

REPL help wanted

Comentários muito úteis

Ainda me parece perverso que apoiemos esta

julia> <strong i="6">@code_native</strong> 1 + 2
    .section    __TEXT,__text,regular,pure_instructions
; ┌ @ int.jl:53 within `+'
    leaq    (%rdi,%rsi), %rax
    retq
; └
; ┌ @ int.jl:53 within `<invalid>'
    nopw    %cs:(%rax,%rax)
; └

e isto

julia> <strong i="10">@code_llvm</strong> 1 + 2

;  @ int.jl:53 within `+'
define i64 @"julia_+_13402"(i64, i64) {
top:
  %2 = add i64 %1, %0
  ret i64 %2
}

e isto

julia> <strong i="14">@code_typed</strong> 1 + 2
CodeInfo(
1 ─ %1 = Base.add_int(x, y)::Int64
└──      return %1
) => Int64

e isto

julia> <strong i="18">@code_lowered</strong> 1 + 2
CodeInfo(
1 ─ %1 = Base.add_int(x, y)
└──      return %1
)

mas não isso

julia> <strong i="23">@code_source</strong> 1 + 2
ERROR: LoadError: UndefVarError: <strong i="24">@code_source</strong> not defined
in expression starting at REPL[23]:1

Podemos introspectar todas as diferentes versões possíveis de um método, exceto aquela que todos que usam Julia sabem ler e escrever. Vou marcar isso como "desejo de ajuda" para indicar que seria uma adição bem-vinda à linguagem ter um modo opcional em que lembramos a representação de origem de uma função. Ele pode ser ativado por padrão no modo interativo, mas desativado por padrão no modo não interativo.

Todos 22 comentários

Se armazenássemos a origem (compactada) de cada definição de método ao executar interativamente, isso poderia ser feito com bastante facilidade e funcionar corretamente mesmo quando os arquivos de origem mudam e mesmo quando a anotação da linha de origem não é perfeita. Isso também ajudaria com o # 265 (veja também esta discussão ), já que você pode usar o código-fonte para recompilar as coisas. Também poderíamos armazenar o AST na forma compactada - seis contra meia dúzia.

Não, não vai ajudar com o # 265. Já temos todas as informações, apenas não se parece mais com o código-fonte. Se você quiser examinar o código original, a melhor maneira é lê-lo no arquivo.

Concordar com @JeffBezanson e isso seria um desvio de R, onde digitar a função sem parênteses exclui o código-fonte. Se a origem tiver mais do que algumas linhas, ela se tornará inutilizável (não há como percorrer a página de saída).

+1 com leitura do arquivo.
Melhor correção https://github.com/JuliaLang/julia/issues/2594 , ele realmente mata os usuários do Windows quando start é um comando nativo cmd e notepad não consegue destacar a sintaxe nem mostrar / pular para o número da linha.
edit é realmente uma função útil, se pudéssemos corrigi-la / torná-la melhor.

Só para entender, é o sentimento aqui que não deveria haver uma função para ecoar o código-fonte dos métodos, mas sim depender de 'editar' para esse propósito? Isso pode não funcionar bem com o notebook da web IPython que está interagindo com um kernel Julia remoto, pois acho que o arquivo será aberto na máquina do kernel em vez da do cliente.

Pelo que vale a pena, em modos interativos, eu gostaria de manter o código-fonte por perto, em vez de confiar no que está nos arquivos. Eu quero ser capaz de ver o código-fonte das coisas que foram inseridas por meio do repl também. Veja também # 3988.

o suporte para pager # 6921 pode negar algumas preocupações sobre a utilidade disso.

Acho isso muito conveniente em ipython (via func?? ), embora - ao contrário do que pode ser o caso de julia - algumas funções estejam ocultas (por exemplo, funções internas / cython).

Além disso, <strong i="5">@less</strong> func(x) faz quase exatamente o que @astrieanna pede (com paginação), mas depende de um $PAGER externo.

<strong i="5">@less</strong> func(x) não funciona para funções interativas, mas isso parece ser um problema que deve ser reformulado em uma edição diferente.

Se os dois recursos a seguir estivessem disponíveis

  • obter código AST pré-reduzido, ou seja, fonte do repl ou em tempo de execução
  • redefinindo / apagando variáveis ​​/ tipos / funções / tipos

então, pode ser possível fazer a listagem / passagem / manipulação / geração do código de tempo de execução.

julia> q=:( function a(i::Int) ; return i+4 ; end ; b=4 ; println(a(b))  )
quote 
    function a(i::Int) # none, line 1:
        return i + 4
    end
    begin 
        b = 4
        println(a(b))
    end
end

julia> function exprdescend(ex) ; if (isa(ex,Expr)) ; println("Descending Expr:",ex) ; println("head:",ex.head); println("args:",ex.args) ; println("type:",ex.typ)  ; for i in ex.args ; exprdescend(i) ; end ; else ; println("*:",typeof(ex),":",ex)  ; end  ;  end
// # ''try it ... long output''

em resposta ao comentário: JeffBezanson comentou em 20 de março de 2013
"Não, não vai ajudar com o # 265. Já temos todas as informações, só não se parece mais com o código-fonte. Se você quiser olhar o código original, a melhor maneira é lê-lo do arquivo . "

Então você acha que isso é possível? O que acontece onde há instruções de inclusão / solicitação e outras coisas?

// # str = read_whole_file_into_a_string(filename)
julia> str="for i in [1,2,3,4] ; println(i) ; end "
julia> s=parse(str)
:(for i = [1,2,3,4] # line 1:
        println(i)
    end)
julia> exprdescend(s)
Descending Expr:for i = [1,2,3,4] # line 1:
    println(i)
end
head:for
args:{:(i = [1,2,3,4]),quote  # line 1:
    println(i)
end}
type:Any
Descending Expr:i = [1,2,3,4]
head:=
args:{:i,:([1,2,3,4])}
type:Any
*:Symbol:i
Descending Expr:[1,2,3,4]
head:vcat
args:{1,2,3,4}
type:Any
*:Int64:1
*:Int64:2
*:Int64:3
*:Int64:4
Descending Expr:begin  # line 1:
    println(i)
end
head:block
args:{:( # line 1:),:(println(i))}
type:Any
*:LineNumberNode: # line 1:
Descending Expr:println(i)
head:call
args:{:println,:i}
type:Any
*:Symbol:println
*:Symbol:i

@hgkamath Infelizmente não entendi a pergunta, mas parece mais adequada para a lista de discussão dos usuários. Por favor, leia as seções de metaprogramação e reflexão do manual.

Outro caso de uso seria @generated functions (de https://groups.google.com/d/topic/julia-users/4pkWhcap1Zg/discussion).

Oh, eu resolvi isso no # 22007?

Mais ou menos - ainda acho que no modo REPL devemos esconder a fonte original das funções para exibição.

Há também a questão de exibir funções anônimas, onde seria bom ser capaz de exibir o AST original.

julia> x -> x+1
(::#5) (generic function with 1 method)

não é super útil. (Veja também discurso .)

no modo REPL

já fazemos basicamente, é apenas irritante acessar:

let h = Base.active_repl.interface.modes[1].hist,
    replno = match(r"REPL\[(\d+)\]", $filename)

    replno === nothing || h.history[h.start_idx + parse(Int, replno[1])]
end

"irritante de acessar" == "não é útil o suficiente para ser considerado resolvido"

Admito que seria útil. Eu frequentemente depuro modificando uma função e executando diferentes variações em REPLs diferentes para compará-los. Às vezes, perco a noção de qual REPL corresponde a qual variação. Seria bom ter a impressão da função atualmente definida.

Um caso de uso em Yao fornecido por https://github.com/MasonProtter/LegibleLambdas.jl

Cada bloco tem um argumento de número de qubits, e não queremos escrevê-lo repetidamente, então ele pode ser executado automaticamente quando você não alimentar este número, por exemplo

julia> using Yao

julia> control(2, 1=>X)
(n -> control(n, 2, 1 => X gate))

Assim, o usuário saberá que não se trata de um bloco, ele precisa desse número de qubits para uma avaliação posterior. Antes de termos LegibleLambdas, é bastante confuso com apenas um número como #42 . Isso também acontece com o Flux quando os otimizadores retornam um lambda antes.

Mas existem muitos casos extremos que não podemos suportar no LegibleLambdas, seria bom se pudéssemos obter essas informações diretamente no REPL com o suporte do compilador em vez do pacote externo.

Ainda me parece perverso que apoiemos esta

julia> <strong i="6">@code_native</strong> 1 + 2
    .section    __TEXT,__text,regular,pure_instructions
; ┌ @ int.jl:53 within `+'
    leaq    (%rdi,%rsi), %rax
    retq
; └
; ┌ @ int.jl:53 within `<invalid>'
    nopw    %cs:(%rax,%rax)
; └

e isto

julia> <strong i="10">@code_llvm</strong> 1 + 2

;  @ int.jl:53 within `+'
define i64 @"julia_+_13402"(i64, i64) {
top:
  %2 = add i64 %1, %0
  ret i64 %2
}

e isto

julia> <strong i="14">@code_typed</strong> 1 + 2
CodeInfo(
1 ─ %1 = Base.add_int(x, y)::Int64
└──      return %1
) => Int64

e isto

julia> <strong i="18">@code_lowered</strong> 1 + 2
CodeInfo(
1 ─ %1 = Base.add_int(x, y)
└──      return %1
)

mas não isso

julia> <strong i="23">@code_source</strong> 1 + 2
ERROR: LoadError: UndefVarError: <strong i="24">@code_source</strong> not defined
in expression starting at REPL[23]:1

Podemos introspectar todas as diferentes versões possíveis de um método, exceto aquela que todos que usam Julia sabem ler e escrever. Vou marcar isso como "desejo de ajuda" para indicar que seria uma adição bem-vinda à linguagem ter um modo opcional em que lembramos a representação de origem de uma função. Ele pode ser ativado por padrão no modo interativo, mas desativado por padrão no modo não interativo.

Funcionalidades semelhantes agora são implementadas por https://github.com/timholy/CodeTracking.jl , que faz parte do Revise.jl. Eu brinquei um pouco com ele e, embora não seja perfeito, funciona na maioria das vezes. A documentação diz que é muito melhor quando também usa Revise.

Isso foi sugerido nesta discussão .

Isso pode ser excelente em combinação com a diferenciação automática (como https://github.com/FluxML/Zygote.jl), já que você pode mostrar a derivada como código julia

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

i-apellaniz picture i-apellaniz  ·  3Comentários

StefanKarpinski picture StefanKarpinski  ·  3Comentários

sbromberger picture sbromberger  ·  3Comentários

ararslan picture ararslan  ·  3Comentários

dpsanders picture dpsanders  ·  3Comentários