Julia: Elimine require, import, y tal vez importell y fusione en using

Creado en 14 ago. 2014  ·  72Comentarios  ·  Fuente: JuliaLang/julia

De la lista de correo:

Stefan:

Creo que deberíamos eliminar por completo la importación y simplemente tener

usando Foo
usando Foo: barra

El primero cargaría y crearía un enlace para Foo, haría que sus exportaciones estuvieran disponibles como enlaces "suaves" (lo que usa ahora). El segundo también cargaría y crearía un enlace para Foo, y haría que la barra estuviera disponible como un enlace "duro" (lo que hace ahora la importación)".

Parece que esto sigue siendo un punto de confusión para los recién llegados.

breaking design modules speculative

Comentario más útil

Me gusta la simetría de import y export . (Como alguien señaló en alguna parte).

Todos 72 comentarios

También necesitaríamos la funcionalidad import Foo alguna manera, donde solo obtienes Foo y nada más

using Foo: ?

using Foo: Foo ?

Para vincular Foo al módulo Foo (y nada más):
import Foo
Para vincular Foo al módulo Foo, x a Foo.x, y a Foo.y
import Foo: x, y
Para enlazar Foo al módulo Foo, y todos los nombres exportados de Foo están enlazados sin calificar:
import Foo: *

Esto podría ser using en su lugar, pero siento que esto está más en el espíritu de import .

Esto también elimina la distinción entre incluir algo en el alcance y hacerlo disponible para la extensión del método. Personalmente, creo que eso es algo bueno y hace que el sistema de módulos sea más comprensible, pero quería asegurarme de que lo mencionamos.

Hay un caso sólido de que una construcción que hace que todos los enlaces exportados de un módulo estén disponibles debería hacerlos enlaces suaves ( using ) y no enlaces duros ( importall ). Supongamos que el módulo A usa el módulo B y define foo(::Any) . Si una versión posterior del módulo B también define y exporta foo(::Any) , no querrá que se golpeen entre sí. Tampoco desea que el módulo B defina foo(::Int) y que el módulo A a veces llame a ese método en lugar del método que definió porque es más específico, o tener que enumerar todos los identificadores que desea del módulo B para evitar importar un único identificador conflictivo.

Pero si ha enumerado explícitamente los identificadores que desea importar, entonces nunca hay una razón para dar un enlace flexible. O no va a definir nuevos métodos de un identificador, en cuyo caso los enlaces fuertes y suaves tienen un comportamiento idéntico, o va a definir nuevos métodos, en cuyo caso un enlace suave es equivalente a ningún enlace y si lo desea ese comportamiento, simplemente debe eliminar el identificador de la lista.

En resumen, me gusta la propuesta de @StefanKarpinski . Conceptualmente, necesitamos enlaces fuertes y suaves, pero podemos obtener todos los comportamientos útiles con una sola palabra clave.

Entiendo tu argumento. En ese caso, me gusta su propuesta de que using Foo: (con los dos puntos pero sin elementos) parece conceptualmente coherente. Los dos puntos indican que va a limitar qué símbolos extraer, pero simplemente no enumera ninguno.

El : final vacío parece un poco divertido, pero creo que la gente se acostumbrará

El problema con los dos puntos finales vacíos es que actualmente buscamos un nombre en la línea siguiente; en otras palabras, se considera que using Foo: está incompleto.

Relacionado: #4600

Sé que la práctica actual de Julia es importar todo a la
espacio de nombres del módulo actual, pero realmente creo que todavía debería
ser una forma simple y natural de importar solo el nombre de un módulo.

También creo que la sobrecarga actual de usar Foo y usar Foo.Bar es
problemático; se ve dentro de Foo pero no dentro de Foo.Bar (a menos que Foo.Bar esté
un módulo?)

Creo que en la propuesta de Stefan, esto se aborda con

usando Foo: Foo

El jueves 14 de agosto de 2014, toivoh [email protected] escribió:

Sé que la práctica actual de Julia es importar todo a la
espacio de nombres del módulo actual, pero realmente creo que todavía debería
ser una forma simple y natural de importar solo el nombre de un módulo.

También creo que la sobrecarga actual de usar Foo y usar Foo.Bar es
problemático; se ve dentro de Foo pero no dentro de Foo.Bar (a menos que Foo.Bar esté
un módulo?)


Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/JuliaLang/julia/issues/8000#issuecomment-52202142 .

@kmsquire, pero ¿qué sucede si hay un módulo Foo dentro del módulo Foo? Eso es lamentablemente ambiguo.

Eso es un error de diseño (y provoca una advertencia), por lo que no importa

Me gusta la versión propuesta por @ssfrr por encima de la mejor.

¿Existe una convención subyacente de que los paquetes tienen un único punto de entrada al módulo? -- Para import Foo significa importar el módulo Foo del paquete Foo . Cualquier otro módulo debe ser un submódulo de Foo . Lo que significa que hay una correspondencia implícita entre el paquete Foo, Foo.jl en el paquete y los module Foo dentro de él.

Pregunto porque me pregunto si import también podría usarse para la importación local/relativa. Digamos que un proyecto tiene src/foo.jl y src/bar.jl luego en foo.jl :

import bar

importaría desde src/bar.jl . Esta es una mejora con respecto al uso include porque funciona dentro del sistema de módulos.

Sí, se espera que los paquetes, los puntos de entrada de paquetes y los módulos sean uno a uno. Puede romper esta convención si lo necesita, pero no hay mucha necesidad de hacerlo ya que los módulos son solo para nombrar y no son unidades funcionales. La idea que está mencionando es #4600 excepto que la sintaxis para una importación relativa es import .bar mientras que import bar es una importación absoluta.

@StefanKarpinski ¿ import .bar realmente busca "bar.jl" en el directorio local? Tenía la impresión de que import .Bar se refería solo a un submódulo de un módulo principal ya existente.

ACTUALIZACIÓN: Duh, eso es lo que propones en #4600. Lo siento.

:+1:. Si vamos a hacer esto, 0.4 sería un buen momento para hacerlo.

:+1: Continúe y limpie algo de esto (¡para 0.4!)

en lugar de tener dos mecanismos de importación para distinguir entre extender o no, ¿por qué no señalar la extensión en el sitio de la extensión a través de una palabra clave o anotación en la definición de la función, por ejemplo, anular la palabra clave antes de la función? Similar a la anotación @Override en Java.

Esto tiene la ventaja de que uno ve claramente que la función anula a otra (y por lo tanto es un método)

Eso ya es posible, @ssagaert. Lo hace escribiendo explícitamente el nombre del módulo en la definición de la función (por ejemplo, Base.print(…) = … ), y parece ser un estilo en el que mucha gente está convergiendo. El único punto conflictivo es que la sintaxis no funciona para todos los nombres posibles (como .+ , etc.).

(Aparte, tenga cuidado de encerrar las macros con acentos graves `` para evitar molestar a otros usuarios de GitHub).

:+1: a la sugerencia de @ssagaert de usar @override , el mensaje de error original cuando uno intenta extender un método sin importarlo primero dice así:

ERROR: error in method definition: function Foo.x must be explicitly imported to be extended

Entonces, ¿quizás @extend podría ser más adecuado? No soy un hablante nativo de inglés, pero entiendo que _anular_ significa algo como: cancelar, anular, invalidar, negar, anular, anular, descontinuar, etc.

Creo que esto es más explícito:

<strong i="13">@extend</strong> Base.show(...) = ...

Que:

import Base: show

# ... several lines here

Base.show(...) = ...

@Ismael-VC Base.show(...) = ... ya funciona sin importar nada. import solo es necesario si desea que show(...) = ... extienda Base.show .

@ Ismael-VC anular la palabra fue solo una sugerencia. Puede extenderse o cualquier otra cosa significativa. Además, no debería haber @ ya que en julia eso significa que es una macro ( @Override se refiere a Java donde es una anotación).

@simonster ¡ gracias, no sabía eso!

@ssagaert , ¿te refieres a una palabra clave? Intenté algo como esto, pero todavía apesto en macro-foo:

module Extend

export <strong i="9">@extend</strong>


macro extend(x)
    mod = x.args[1].args[1].args[1]
    met = x.args[1].args[1].args[2]
    imp = :(Expr(:import, $mod, $met))
    :(Expr(:toplevel, $imp, $(esc(x))))
end


end

Sé que esto no es general, aún así no puedo hacer una expresión que devuelva lo que hace Parse:

julia> using Extend

julia> type Foo end

julia> <strong i="13">@extend</strong> Base.show(x::Foo) = Foo
:($(Expr(:toplevel, :($(Expr(:import, Base, :show))), show)))

julia> parse("import Base.show; Base.show(x::Foo) = Foo")
:($(Expr(:toplevel, :($(Expr(:import, :Base, :show))), :(Base.show(x::Foo) = begin  # none, line 1:
            Foo
        end))))

Creo que una macro @extend general y funcional no cambiaría la semántica del mecanismo de importación.

@Ismael-VC Lo hice pero también me gusta el 'truco' de @mbauman .

Creo que sería bueno poder usar . solo para significar "esto". por lo que podrías escribir expresiones como:
import Base: . (lo que significa importar Base.Base )
o
using ..

estoy bastante seguro de que requeriría https://github.com/JuliaLang/julia/pull/11891#issuecomment -116098481 sin embargo. tal vez sea suficiente permitir espacios antes de . , pero no después, para resolver el caso de ambigüedad.

Creo que require ya está en desuso. Podría ser bueno agregar una notación de puntos más flexible en la importación de módulos en 1.0, pero dudo que vayamos a cambiar algo aquí en 0.6 congelación de características

Después de un montón de discusión sobre esto ayer, me inclino por algo como las propuestas en https://github.com/JuliaLang/julia/issues/8000#issuecomment -52142845 y https://github.com/JuliaLang/julia/ problemas/8000#emisióncomentario -52143609:

using A: x, y    # hard imports x and y from A

using A: A       # hard imports just the identifier `A`

using A: ...     # soft imports all of A's exports

using A     # equivalent to `using A: A, ...`

using A.B   # A.B must be a module. equivalent to `using A.B: B, ...`

using A: ..., thing1, thing2    # import all exports plus some non-exported things

La alternativa sería mantener import en lugar de using :

import A             # hard binding for the module `A`
import A: ...        # soft bindings for all names exported by `A`
import A: x, y       # hard bindings for `x` and `y` from `A`
import A: x, y, ...  # equivalent to doing both of the previous two

La regla general para determinar si un enlace es duro o suave sería simple: cualquier nombre solicitado explícitamente es un enlace duro, cualquier enlace dado implícitamente es suave.

Editar: existe el deseo de agregar a este esquema alguna forma abreviada de import A; import A: ... que es aproximadamente lo que hace actualmente using A (la única diferencia es que using A actualmente importa suavemente A ); esto podría continuar siendo using A o se ha propuesto import A... .

Sí, creo que también es una buena propuesta. Básicamente se reduce a si uno prefiere using o import .

Anexo, podríamos mantener using A como abreviatura de import A; import A: ... , junto con eliminar el comportamiento actual de que cada módulo tiene un enlace exportado por sí mismo, razón por la cual using A causa debe haber un enlace flexible a A disponible.

Estaría bastante decepcionado si aún tuviéramos varias palabras clave después de todo esto.

Me gusta la simetría de import y export . (Como alguien señaló en alguna parte).

Hacer siempre "enlaces duros" no parece ser el valor predeterminado más seguro en términos de extensión de método. Existía la propuesta relacionada, pero algo separada, de requerir la calificación del módulo para la extensión del método que podría eliminar la necesidad de "enlaces duros".

También se necesitan enlaces duros para este propósito:

import Package: x
x = 1   # gives an error

Y lo que es más importante, esta propuesta no siempre haría enlaces duros, solo para las cosas que enumera explícitamente. Requerir que uno diga import en lugar de using no agrega mucha seguridad.

La seguridad proviene de la mayoría de los lugares que no están haciendo una extensión de método que puede salirse con la suya con un enlace suave. Creo que todavía deberíamos tener una forma de solicitar un enlace suave específico sin tener que importar todas las exportaciones de un paquete (u obtener un enlace suave específico que no se exporta).

¿Todavía tenemos la advertencia por sobrescribir un enlace importado?

Creo que exigir la calificación del módulo es una buena idea. En este momento, para saber si se está introduciendo una función o se está extendiendo un método, debe buscar en el contenido del paquete completo una instrucción import A: func .

¿Todavía tenemos la advertencia por sobrescribir un enlace importado?

Creo que en realidad es un error ahora.

Hay otra propuesta flotando que mantiene ambas palabras clave, pero aún simplifica un poco las cosas:

  1. Agregue la sintaxis import A: ...
  2. Obsoleto using A:
  3. En using A.B , requiere que A.B sea un módulo y documente que es una forma abreviada de import A.B; import A.B: ... .

De esta manera, todo se puede hacer con solo import , pero using X está disponible para mayor comodidad. También sería especialmente fácil hacer la transición.

Por cierto, using parece inconsistente como publiqué aquí . Si mantenemos using , debería cambiarse el nombre a use si es posible.
Creo que deberíamos exigir la cuantificación del módulo al extender funciones, ya que su significado es mucho más claro que el patrón de importar y luego extender.

Mi enfoque favorito:

  • Requerir un prefijo de módulo explícito para la extensión
  • Si solo desea el nombre del módulo y no sus símbolos, use using A: A
  • Eliminar import y el tipo de enlace que crea

Cosas que deberían suceder para implementar https://github.com/JuliaLang/julia/issues/8000#issuecomment -327512355:

  • cambie el comportamiento de using A a importación fuerte A
  • quitar soporte para using A: x
  • elimine el soporte para using A.x donde x no es un submódulo
  • elimine el soporte para import A.x donde x no es un submódulo
  • agregue soporte para la sintaxis ... a import

using A: x se usa a menudo y es muy útil. Está diciendo que quiere x en su espacio de nombres pero no quiere extenderlo. En import A: x , está diciendo que desea poder extender x . Hay una distinción significativa entre tener una función disponible para su uso y poder extenderla.

Ahora que lo pienso, diría que el problema más grande aquí es que using A.B hace dos cosas: si B es un módulo, importa suavemente todas sus exportaciones y, de lo contrario, solo importa suavemente B . Creo que deberíamos arreglar eso, y hacer que using A.B solo se permitan para módulos, y tener using A: a, b para la importación suave de enlaces específicos uno a la vez.

Preferiría que hubiera una forma de escribir import A: x en lugar de que fuera equivalente a import A.x .

Voto por import A: x ya que también podemos hacer; import A: x, y, @z pero import A.x, A.y, a.@z se vería feo.

¿La eliminación de esto de 1.0 significa que nos quedarán using y import para 1.0? Eso es un poco desafortunado en mi opinión.

Qué tal si:

  • Forzar prefijo de módulo para extensión. Esto hará que el código sea más claro de que se pretende una extensión en lugar de que sea accidental de una importación en algún otro archivo.
  • using A se convierte en import A: ...
  • using A.X ( X es un módulo) se convierte en import A.X: ...
  • using A: X ( X no es un módulo) se convierte en import A: X
  • import A: X no se cambia pero no se puede extender automáticamente X (ver el primer punto)
  • elimine la palabra clave using

¿Me estoy perdiendo algún caso de uso? Tal vez esto ya fue sugerido...

Lo que me gusta de ser explícito sobre el módulo al extenderlo es que la extensión se vuelve mucho más local. En este momento, cuando se extiende un método, es común colocar la importación muy cerca de la parte superior del módulo (¡que podría estar en un archivo completamente diferente!). Cuando se elimina el método extendido, la declaración de importación generalmente se olvida.

Creo que es esencialmente lo mismo que mi propuesta anterior, que obtuvo bastante apoyo. @JeffBezanson realmente quiere mantener using A al menos por facilidad de uso y using A: x porque aparentemente (no estoy convencido de esto), es un caso de uso importante poder importar un enlace en tal manera que no se puede extender. Hay algunas propuestas para ir en la otra dirección y reemplazar import con using pero ninguna de ellas realmente obtuvo mucha tracción ( import parece más fundamental).

Creo que la diferencia está en:

  • importar A: x, y # enlaces duros para x y y desde A

Asumiendo que el significado de "enlace duro" es tal que puede extenderlo sin prefijo de módulo, en mi versión no hay enlaces duros. Si desea extender, el módulo lo antepone exactamente donde lo extiende. No hay declaraciones espeluznantes import en otros archivos que cambien el significado de si algo es una extensión o no.

y using A: x porque aparentemente (no estoy convencido de esto), es un caso de uso importante poder importar un enlace de tal manera que no pueda extenderlo.

¿El prefijo del módulo forzado no se ocupa de eso? ¿O estamos hablando de módulos no como:

module M
    x = 1
end

Tanto import M: x; x = 2 como using M: x; x = 2 dan el mismo mensaje de advertencia, así que no veo cuál es el problema...

Mantener using A para facilitar más de import A: ... parece un poco excesivo en mi opinión.

¿El prefijo del módulo forzado no se ocupa de eso?

Sí; si tuviera que calificar funciones para extenderlas, este punto sería irrelevante.

Seguir usando A para facilitar la sobreimportación A: ... parece un poco excesivo en mi opinión.

Yo lo veo al revés; hacer que las personas cambien de using A (que es agradable y corto y todos estamos bastante acostumbrados) a import A: ... solo para satisfacer el requisito artificial de que solo haya 1 palabra clave es excesivo.

Al leer el hilo, parece que el principal valor de tener dos palabras clave es diferenciar entre enlaces que pueden extenderse o no (enlace duro). Con eso en mente, creo que hay dos soluciones viables:

  • 1 palabra clave + requiere prefijo para extender
  • dos palabras clave, una para uso normal (sin extensión) y una segunda para uso extendido, sugiero using y extending en este caso. import está bien, pero extending hace que la razón de la existencia de una segunda palabra clave sea obvia

En cualquier caso, sugiero que using debería ser como está ahora con la adición de uno de los siguientes para vincular solo el módulo Foo :

  • using Foo: nothing (funciona ahora)
  • using Foo: Foo (funciona ahora)
  • using Foo: (se puede agregar más adelante)

Entonces extending debería comportarse de manera idéntica a using con la única diferencia de que puede extender los enlaces traídos con extending , y posiblemente rechazar extending Foo para que tenga para ser explícito.

Comportamiento actual

| | poner a disposición (usando) | hacer extensible (importar)|
| ------------------- | -------------------------- | ------------------------------------- |
| único módulo | using module: module o using module: nothing | import module |
| todo lo exportado | using module (efecto secundario: actúa como import module también) | ? |
| cosas particulares | using module: x,y | import module: x,y |

Sugerencia

| | poner a disposición (usando) | hacer extensible (importar) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module | import module |
| todo lo exportado | using module: * | import module: * |
| cosas particulares | using module: x,y | import module: x,y |

Lo bueno de esto es que importar más corresponde a escribir más. Es decir, comienza con using module y si desea importar una variable directamente al espacio de nombres, agrega : x en lugar de eliminar nothing o module . También significa que lo más corto que escribes incluye lo mínimo.

También puede hacer using: *,x para que todo lo exportado esté disponible y x que no se exportó.

Compromiso de compatibilidad con versiones anteriores:

| | poner a disposición (usando) | hacer extensible (importar) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module: | import module: |
| todo lo exportado | using module: * | import module: * |
| cosas particulares | using module: x,y | import module: x,y |

mantener alrededor using module y import module con el comportamiento actual para la compatibilidad con versiones anteriores, pero dejarlo obsoleto.

@FelixBenning : import Module actualmente (por sí mismo) no hace que nada sea extensible más que using Module , solo carga el código y trae Module (y nada más) al espacio de nombres .

Solo para reflejar lo que he dicho en Slack y para que no desaparezca en el agujero de Slack:

No creo que hacer que using X: * sea el valor predeterminado para hacer que todos los elementos exportados estén disponibles en lugar de solo using X hará que las personas desconfíen necesariamente más de lo que importan. Lo sé, señalar cómo lo hacen otros se considera de mala educación, pero Python básicamente tiene esa semántica con sus import X y import X: * , pero su ecosistema está lleno de esas importaciones estelares 🤷‍♂️ (y son famosos por odiar eso) No creo que el texto marginalmente más largo que uno tiene que escribir impida que las personas hagan lo que consideran más conveniente: simplemente importe/utilice todo y deje que el compilador lo resuelva. Por eso desconfío de la varita mágica de hacer que la gente escriba esa estrella explícitamente.

Además, import module: * y using module: * no están disponibles para el significado propuesto. Ya tiene un significado ya que * es un identificador de Julia válido y se puede importar/usar como + o la palabra mul .

@tpapp o he entendido mal la documentación nuevamente, o import Module hace que Module.x sea extensible. Mientras que using Module: x no hace que Module.x sea extensible. Por lo tanto import Module hace que algo esté disponible para la extensión y using Module también lo hace, por lo que noté que el uso tiene ese efecto secundario.
grafik
(de https://docs.julialang.org/en/v1/manual/modules/)

Realmente no importa cuál de nosotros tenga razón; en cualquier caso, la situación actual es obviamente un desastre si ni siquiera podemos averiguar qué hace todo.

Buen punto de @mbauman : me olvidé de eso. Realmente no me importa el * solo la estructura de tener using refleja las cosas que hace import con la diferencia de importar vs usar si las cosas se vuelven extensibles o no. Entonces, si hay un símbolo más apropiado - all , __all__ , everything , exported , ...? Estoy a favor. Solo creo que importar más probablemente debería reflejarse escribiendo más.

Pero si no quieres eso en absoluto, por supuesto también puedes optar por

| | poner a disposición (usando) | hacer extensible (importar) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module: module | import module: module |
| todo lo exportado | using module | import module |
| cosas particulares | using module: x,y | import module: x,y |

o

| | poner a disposición (usando) | hacer extensible (importar) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module | import module |
| todo lo exportado | using module: module | import module: module |
| cosas particulares | using module: x,y | import module: x,y |

Pero cualquiera que sea el resultado final, debe ser consistente. Y por el momento simplemente no lo es.

using A hace que A.f sea extensible, _no_ f por sí mismo. Para extender _solo_ f sin declarar desde qué módulo desea que se extienda, debe import A: f explícitamente. De lo contrario, aún tendrá que calificarlo.

Verifique lo siguiente para la semántica de using

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> using .A

julia> f()
"no args in A"

julia> f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[5]:1

julia> f(x) = "one arg where?"
ERROR: error in method definition: function A.f must be explicitly imported to be extended
Stacktrace:
 [1] top-level scope at none:0
 [2] top-level scope at REPL[6]:1

julia> A.f(x) = "one arg where?"

julia> f(1)
"one arg where?"

Y esto para la semántica de import :

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> import .A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[4]:1

julia> A.f()
"no args in A"

julia> f(1)
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[6]:1

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[7]:1

julia> f(x) = "one arg where?"
f (generic function with 1 method)

julia> f(1)
"one arg where?"

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[10]:1

julia> A.f(x) = "one arg where in A"

julia> A.f(1)
"one arg where in A"

@FelixBenning : sí, creo que lo entendiste mal. FWIW, creo que "... hace que Foo.x sea extensible" es una forma confusa de abordar la distinción: siempre puede definir métodos para nombres de funciones completamente calificados. Lo que sucede con using Foo: x es que Foo en sí mismo no se incluirá en el espacio de nombres.

Por cierto, al volver a leer este tema, me pregunto si #25306 nos llevó a una especie de óptimo local, y la única decisión que queda es si necesitamos importaciones no extensibles en el espacio de nombres (actualmente using Foo: f ). Pero dado que eso se está rompiendo, el capítulo del manual tal vez se beneficiaría de una reescritura mientras tanto, muchos usuarios encuentran todo confuso.

Así es como abordaría el status quo en los documentos:

  1. using M carga el módulo y trae M y sus símbolos exportados al espacio de nombres. Este es el único caso en el que las exportaciones importan.
  2. una vez que tenga M , puede usar nombres calificados como M.y para (a) acceder a símbolos no exportados y (b) agregar métodos a funciones, independientemente de si se exportan o no
  3. Julia le impide agregar métodos a las funciones a menos que use nombres calificados como M.f , o...
  4. ... import M: f , entonces puede usar f al definir métodos.
  5. import M y using M: x son para traer solo M o x (y no M ), respectivamente, al espacio de nombres, respectivamente. A algunas personas les gusta usar estos formularios en el código del paquete para asegurarse de que sus espacios de nombres se mantengan limpios (enlace guías de estilo relevantes aquí).

El orden refleja más o menos los casos de uso encontrados con un uso más avanzado. Todo lo anterior se aplica a los submódulos, con M.A en lugar de M .

¿Qué pasa con esto?

  • using M trae M al alcance
  • using M: x trae M.x al alcance (como x )
  • using M: ... trae todos los símbolos exportados de M al alcance
  • using M: x, ... incluye todos los símbolos exportados de M y también x (que podría no estar exportado)
  • No hay import . Debe usar el nombre calificado para extender una función. (O using M; const foo = M.foo como uno podría hacer ahora).
  • En todo lo anterior, M también podría ser un submódulo, por ejemplo, Foo.Bar y x también podría ser x as y con el significado actual.

O usamos import en lugar de using lo que hace que sea igual a https://github.com/JuliaLang/julia/issues/8000#issuecomment -355960915.

Un uso muy común (especialmente en "scripts", pero también paquetes para ciertos estilos es)

using Foo, Bar, Baz

y simplemente confiando en los símbolos exportados. Habría algún beneficio en mantener esto de la manera más simple, posiblemente tan simple como lo es ahora.

@tpapp

Creo que lo entendiste mal. FWIW, creo que "... hace que Foo.x sea extensible" es una forma confusa de abordar la distinción: siempre puede definir métodos para nombres de funciones completamente calificados.

Bien, ¿puedo extender Foo.x después using Foo: x ? Porque si es así, entonces la documentación no está completa (ver mi captura de pantalla). Si no, entonces he entendido perfectamente cómo funcionan estas declaraciones y, obviamente import Foo hizo algo para hacer que Foo.x sea extensible. Por lo tanto, literalmente hace que Foo.x estén disponibles para la extensión. En todos los sentidos de estas palabras. Claro que no hace que x esté disponible para la extensión, pero para eso es import Foo: x

Bien, ¿puedo extender Foo.x después using Foo: x ?

No, a menos que traiga Foo al espacio de nombres de alguna manera (por ejemplo using Foo ).

He entendido perfectamente cómo funcionan estas declaraciones.

No estoy muy seguro de esto; sin embargo, si tiene preguntas sobre módulos y espacios de nombres, utilice amablemente el foro de Discourse.

obviamente import Foo hizo algo para hacer extensible Foo.x

Solo en el sentido de que debe poder referirse a una función de alguna manera usando un nombre calificado antes de agregarle métodos.

entonces la documentación no está completa

Creo que lo es, de una manera peculiar. En ese ejemplo particular, si todo lo que hace es using MyModule: x, p ; entonces no hay métodos disponibles para la extensión, por lo que la tabla es correcta.

Estoy de acuerdo en que podría escribirse mejor, como dije anteriormente. Mucha gente que no está acostumbrada a los espacios de nombres lo encuentra confuso. Y, TBH, todo el circo using / import es un poco confuso, de ahí este problema.

@tpapp

De acuerdo, aquí está la cosa: no es absolutamente obvio que pueda extender cada función con el nombre completo si el módulo está en el espacio de nombres. Sé que este es el comportamiento actual, pero no creo que alguien que no lo sepa ya asuma eso. Especialmente porque estar en el espacio de nombres no siempre significa que también sea extensible. Si hago using module:x no puedo extender x . Si bien puedo extender x , si uso import module:x . Por lo tanto, es una suposición razonable que la diferencia entre using y import es si puede o no extender las funciones importadas.

Usando esta suposición, tendría sentido si using module solo permitiera el uso de module.x pero no permitiera la extensión de module.x . Mientras que import module permitiría la extensión de module.x . Entonces, si toma esta suposición y lee la documentación, encontrará que hay dos cosas incorrectas.

  1. using module te permite extender module.x . Por lo tanto, desde la perspectiva de los estudiantes, using module tiene el efecto secundario de que también actúa como import module , es decir, hace que module.x sea extensible. Y hacer que las cosas sean extensibles es una cosa import , no una cosa de using
  2. en contraste con import , using no solo hace disponible module , sino todo lo que contiene.

Así que esto es lo que traté de representar con mi tabla. Si se usan dos palabras diferentes (importar/usar), entonces deberían hacer dos cosas diferentes y eso debería quedar claro. Si using a veces permite la extensión y otras veces no, ese no es un comportamiento predecible.

Una buena alternativa es eliminar la importación por completo como sugiere @martinholters . Simplemente no puede tener dos palabras que se usan al azar para ciertas cosas. Si import significa hacer que las cosas sean extensibles al importar ciertas funciones, mientras que using module: foo no lo permite, entonces debería ocurrir el mismo comportamiento cuando solo incluye module en el espacio de nombres.

El hecho de que pueda extender todo por su nombre completo en este momento si el módulo está en el espacio de nombres, no significa que esto sea algo obvio o sencillo.

O estar en el espacio de nombres es suficiente, entonces también debería poder extender x si lo incluí en el espacio de nombres con using module:x o estar en el espacio de nombres no es suficiente, y luego también debería no podrá extender module.x cuando use el comando using .
O tercera opción: no hay import y simplemente tienes que extender las funciones con su nombre completo todo el tiempo.

No es absolutamente obvio que puede extender cada función con el nombre completo si el módulo está en el espacio de nombres.

Estoy de acuerdo, por lo que abogo por una reescritura de los documentos. Sin embargo, eso es tangencial al problema actual. Sería mejor mantener separadas (1) las propuestas para mejorar la documentación de la API actual y (2) las propuestas para rediseñarla, incluso si las dos están un poco relacionadas. Planeo hacer una PR para (1) pronto.

@tpapp No puede documentar algo que inherentemente no tiene sentido. Ninguna de estas documentaciones es correcta:

  1. "Puede extender cualquier cosa que esté en el espacio de nombres" (porque using module:x no le permite extender x )
  2. "Estar en el espacio de nombres no es suficiente para la extensión, es por eso que existen las palabras separadas using y import " (falso porque para los nombres completos, de hecho, es suficiente estar en el espacio de nombres; ser suficiente fue mi sugerencia)
  3. "Puede extender cualquier cosa en el espacio de nombres por su nombre completo" (lo que requeriría import module:x para dejar de existir para ser la verdad completa - propuesta de @martinholters )

Cualquiera de estos sería aceptable. Pero ninguno de estos es en realidad la realidad. La realidad es que actualmente se usan dos palabras diferentes para "importar cosas", pero en realidad no tienen un caso de uso distinto. Es como un bucle for que a veces se comporta como un bucle while si itera sobre valores booleanos. Ninguna cantidad de documentación hará que eso no sea confuso.

No se puede documentar algo que inherentemente no tiene sentido.

La API de los módulos actuales está bien definida, por lo que se puede documentar (ya lo está, solo creo que debería ser mejor).

Ninguna de estas documentaciones es correcta:

Por favor, sea tan amable de esperar mi PR (o el de otra persona) y comentar sobre el texto real que estará allí. Presentar documentación hipotética y luego afirmar que es incorrecta es solo agregar ruido a esta discusión.

@tpapp tal vez debería haber dicho,

no puede documentar algo de una manera no confusa que no tiene sentido inherentemente

Quiero decir, en cierto sentido, el código es toda la documentación que necesita, ¿verdad? ¿Qué está mal con eso? El tiempo que se tarda en digerirlo. Y actualmente no veo una forma breve de describir cómo funciona, porque está plagado de excepciones. Excepciones que no deberían estar allí en primer lugar.

¿Realmente no ves lo que estoy tratando de transmitir?

Creo que hay un acuerdo general de que la situación actual es innecesariamente compleja y está mal documentada. Y sin duda, simplificar la maquinaria facilitará su documentación. Pero tal simplificación se romperá, por lo que no se puede hacer antes de 2.0. Entonces, ¿su punto es que la mejora de la documentación anterior no tendría sentido? Entonces no estoy de acuerdo. Veo dos problemas separados: la simplificación se realizará con 2.0 (de lo que trata este problema) que (con suerte) incluirá las actualizaciones de documentación necesarias y mejorará la documentación del funcionamiento actual que al leer este hilo parece muy necesario pero es otro problema .

Después de hacer #38271, creo que las preguntas pendientes son

Cómo manejar la adición de métodos a funciones en otros módulos

  1. siempre requieren nombres completos ( Foo.bar() = ... ),
  2. también permita cuando el símbolo esté dentro del alcance ( using Foo: bar; bar() = ... )
  3. (2), pero dentro del alcance de una manera especial ( status quo , import Foo: bar; bar() = ... )

Qué sintaxis maneja las listas de exportación y solo el nombre del módulo

  1. using Foo trae todos los símbolos exportados en Foo al alcance, import Foo solo el módulo ( status quo )
  2. using Foo: ... o alguna otra sintaxis similar, luego using Foo solo el módulo.

(1|2) & 2 permitiría la unificación en una sola palabra clave, ya sea using o import , a costa de perder una sola línea

using LinearAlgebra, Random, StaticArrays

No estoy seguro de que valga la pena, incluso sin considerar el cambio de última hora.

Este no es uno de esos problemas que ofrecen una solución limpia que el diseño original acaba de perder; hay compensaciones. Esperaría y vería si una mejor documentación puede mejorar la experiencia del usuario manteniendo la configuración actual (1.0).

Para 2.0, creo que sería bueno simplemente una limpieza de la sintaxis para que sea más consistente y descriptivo de lo que realmente está sucediendo. Algo como:

| Antes | Después |
|-|-|
| using Foo | useall from Foo |
| import Foo | use Foo |
| using Foo: a | use a from Foo |
| import Foo: a y import Foo.a | extend a from Foo |

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

Keno picture Keno  ·  3Comentarios

yurivish picture yurivish  ·  3Comentarios

tkoolen picture tkoolen  ·  3Comentarios

musm picture musm  ·  3Comentarios

StefanKarpinski picture StefanKarpinski  ·  3Comentarios