Open asanzo opened 2 years ago
+1 a toda la propuesta de @asanzo
Como cosa extra agrego tres ideas:
Hace rato venimos hablando de agregar un package wollok.reflection
o wollok.reflect
y a mi me parece la opción superadora para lidiar con cosas como kindName
.
Podríamos armar una listita con todas esas cosas medio reflectivas que tienen los objetos en lang
y meterlas como funciones de un/os objeto/s en reflection
(O sea, básicamente implementar Mirrors).
No tiene porqué hacerse de golpe, puede hacerse de a poco, pero eso permitiría tener (y de ser necesario, delegar en) estas operaciones sin que le aparezcan en el autocompletar al alumno.
No estaría bueno que el método que se usa en todos los otros lenguajes y que el pibe seguro anticipa usar sea el que va a imprimir el mensaje "humanizado" y dejar el método más raro para los usos internos? No sé cual recomendar si toString
o show
o printString
, pero creo que estaría bueno minimizar la sorpresa.
En esa linea de pensamiento, estaría bueno considerar cosas cómo ¿Qué variante (humana/otra) debería usar un objeto cuando se lo concatena? Estoy tentado de pensar que la humana, pero, para los strings eso sería agregar las "
y romperías todo. Lo que se me ocurre es que, tal vez no necesitamos una variante "no-humana" y alcanza con invertir el control en dónde haga falta. Por ejemplo, el append (que si no recuerdo mal es native), en lugar de delegar al toString, podría decidir él en base al parámetro que recibió: Si es un string, lo pone sin comillas y maneja cualquier otro caso raro y, el resto, lo delega al toString.
En definitiva mi duda es si tenemos más usos para un mensaje human-unfriendly, porque parece medio anti-intuitivo y me pregunto si vale la pena.
Estoy un poco oxidado con algunas cosas, pero de la excelente descripción de @asanzo a mí me surge esto, trato de relacionarlo con lo que propuso él.
- [ ] En ese último sentido, podríamos dejar el
method toString() = self.kindName()
pero podemos hacer unmethod printString() native
que se encargue de hacer que para un WKO diga su nombre, para un objeto no nombrado diga "un Objeto", y para el resto diga "un/a NombreDeClase".
Me hace ruido el toString = kindName, porque parecen referir a cosas distintas:
En algún default, el toString podría estar basado en el kindName, pero no ser igual. Por ejemplo el toString de new Perro()
podría ser un/a Perro
y su kindName sería Perro
.
Siguiendo lo que dice el comentario de Nico sobre "minimizar la sorpresa", me gusta que toString sea la representación primaria de cualquier objeto. (Aunque esto tiene algunas sutilezas, porque posiblemente la "representación primaria" que se llama automáticamente no sea igual al método que vos podés -o deberías- redefinir. Por ejemplo en Pharo esto es distinto.)
- [ ] Que
printString
sea lo que se espera en consola para imprimir objetos retornados, que es el redefinible. No sé en qué lugares ameritaría además reemplazartoString
porprintString
. Tunearía las documentaciones de ambos métodos para saber cuál usar y cuál redefinir en cada caso. En particular, me gusta la idea de que "toString" tenga la semántica "convertir a string" y que "printString" tenga la semántica "mostrale a une humane este objeto".
Personalmente no veo muchos casos en los que printString sea distinto de toString, salvo justamente los strings. De todas maneras, lo mantendría al menos por ese caso y debería usarse en todos lados que mostremos un objeto en las herramientas para el programador:
El toString básicamente sirve cuando uno concatena strings dentro de su código de negocio. ¿Es así?
- [ ] Borrar toda referencia a
className
. Que en todos los lugares donde se useclassName
se reemplace porkindName
. (me gusta más "kindName" para el nombre de un método que me dice tu identificador estático, porque podés no tener una clase, como en el caso de un WKO o un objeto no nombrado).
Comparto, pero posiblemente como un paso intermedio apuntando a la idea de @nscarcella (y antes @javierfernandes) de incorporar Mirrors.
- [ ] ¿Agregar quizás una validación con warning de no usar kindName? Que diga lo mismo que la de igualar por objeto: "quizás querés usar polimorfismo".
Qué se yo, esto me lleva a discusiones filosóficas sobre los warnings... yo quisiera poder programar sin warnings y entonces si siempre que usás el kindName tira warning... entonces para qué está? El código interno de Wollok si yo lo abro tira warnings? Tal vez si lo pudieras tunear y chequear que no se use dentro de un if entonces sí, a full.
- [ ] Borrar toda referencia a
shortDescription
(prefiero) o bien que la documentación deshortDescription
agregue algo como "e.g. for small places like object diagrams" (Y que el worksheet de wollok-run-client envíe ese mensaje para mostrar su diagrama).
De lo anterior, yo no entendí por qué sería necesario. El diagrama debería usar lo mismo que la consola. Tal vez es una herencia de cuando los toStrings eran más complejos.
En algunos lugares define toString como shortDescription y en otros shortDescription como toString eso no puede ser saludablew.
- [ ] De hecho la implementación actual de printString de collection parece ser más adecuada para una
shortDescription
, y de hecho usakindName
, ojo si decidimos volarlo en lugar de className.
No entendí lo de shortDescription en Collection.
- [ ] La forma de imprimir un Date podría usar formato ISO (YYYY-MM-DD) (cambiar el contrato de su toString) para no tener quilombos de locale.
Puede ser pero sería otra discusión, no sigamos agregando problemas acá.
- Hace rato venimos hablando de agregar un package
wollok.reflection
owollok.reflect
y a mi me parece la opción superadora para lidiar con cosas comokindName
. Podríamos armar una listita con todas esas cosas medio reflectivas que tienen los objetos enlang
y meterlas como funciones de un/os objeto/s enreflection
(O sea, básicamente implementar Mirrors). No tiene porqué hacerse de golpe, puede hacerse de a poco, pero eso permitiría tener (y de ser necesario, delegar en) estas operaciones sin que le aparezcan en el autocompletar al alumno.
+1 a todo eso.
- No estaría bueno que el método que se usa en todos los otros lenguajes y que el pibe seguro anticipa usar sea el que va a imprimir el mensaje "humanizado" y dejar el método más raro para los usos internos? No sé cual recomendar si
toString
oshow
oprintString
, pero creo que estaría bueno minimizar la sorpresa.
+1 también y voto que sea toString, antes propuse cómo combinar toString y printString.
- En esa linea de pensamiento, estaría bueno considerar cosas cómo ¿Qué variante (humana/otra) debería usar un objeto cuando se lo concatena? Estoy tentado de pensar que la humana, pero, para los strings eso sería agregar las
"
y romperías todo. Lo que se me ocurre es que, tal vez no necesitamos una variante "no-humana" y alcanza con invertir el control en dónde haga falta. Por ejemplo, el append (que si no recuerdo mal es native), en lugar de delegar al toString, podría decidir él en base al parámetro que recibió: Si es un string, lo pone sin comillas y maneja cualquier otro caso raro y, el resto, lo delega al toString. En definitiva mi duda es si tenemos más usos para un mensaje human-unfriendly, porque parece medio anti-intuitivo y me pregunto si vale la pena.
Mi idea es que para la concatenación existe el toString. Yo pensaba sólo dos representaciones: printString y toString (además de kindName que es otra cosa). ¿Necesitamos otra representación más? No lo tengo claro.
Igualmente, para mí a esto le falta un detalle técnico para el manejo de errores, ya lo mencionó @asanzo, intento elaborar sobre eso.
Bueno, hay lugares en los que eso no es aceptable, en particular cuando estás tratando de mostrar otro error, dentro de un stack trace por ejemplo, no querés otra excepción. Si el toString se lo dejás reescribir a un estudiante que está aprendiendo... es lógico prever errores.
La opción que planteaba @asanzo entiendo pasa por no usar ese toString, que es redefinible sino otro que no se pueda redefinir (bueno, eso no es exactamente posbible en Wollok, pero es uno que no se debe redefinir). Eso agrega una tercera definición, la llamaré baseToString
. Con este agregado, básicamente todas las definiciones default del lenguaje pasan a baseToString y method toString() = self.baseToString()
.
Hasta aquí entiendo sería seguir la lógica de Alf. Esa lógica implica que en un stack trace yo no puedo llamar al toString, al que considero falible, y en cambio llamaría al baseToString, que viene con el lenguaje y se considera infalible. Esta estrategia no me gusta porque impide que yo vea mis representaciones piolas descriptivas de mis objetos en esos lugares... y en todo caso puede pasar en cualquier lado porque el diagrama de objetos tampoco quiero que se rompa, ni la consola... el toString aparece en muchos lugares delicados, si lo vas a excluir de donde no se puede romper tal vez pierde mucha de su utilidad.
Por eso, en cambio, pienso que deberíamos agregar todavía una representación más (sic), a la que llamaré safeToString()
y tiene como primera propuesta de implementación esto:
method safeToString() {
try { return self.toString() }
catch (...) {
// .... pensar cómo informar el error en algún lado
return self.baseToString()
}
}
De esta manera te evitás la posibilidad del error pero sin perder la posibilidad de tener mensajes "lindos".
Por último, la pregunta en este caso sería cómo queda el printString, que habíamos dicho de definirlo = toString... tal vez es tan fácil como modificarlo a = self.safeToString()
?
@npasserini No me quedó claro quién llamaría a ese safeToString
en lugar del toString
habitual.
Por otro lado, no estoy seguro de si vale la pena agregar un safeToString
que no podemos forzar a que no sobreescriban, sobre todo si es para usarlo en un lugar muy concreto. Yo ahí haría la inversión de control que propuse arriba: Que el toString
de los errores sea un native y metemos ese try-catch ahí. Eso hace que el método no sea redefinible (porque no existe).
Por lo que yo veo el único usuario directo sería el concat
, tal vez se me
esté escapando alguno.
Pero la idea es que todo lo demás use printString
que delega en safeTo String
, por lo que básicamente todo el código interno del lenguaje y/o IDE
termina usándolo.
Esto arma una cadena que es: printString => safeToString => toString => baseToString.
Todo esto es casi desconocido para los usuarios:
On Thu, Jun 30, 2022 at 12:24 AM Nicolás Scarcella @.***> wrote:
@npasserini https://github.com/npasserini No me quedó claro quién llamaría a ese safeToString en lugar del toString habitual. Por otro lado, no estoy seguro de si vale la pena agregar un safeToString que no podemos forzar a que no sobreescriban, sobre todo si es para usarlo en un lugar muy concreto. Yo ahí haría la inversión de control que propuse arriba: Que el toString de los errores sea un native y metemos ese try-catch ahí. Eso hace que el método no sea redefinible (porque no existe).
— Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok-language/issues/140#issuecomment-1170707540, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDLKOKSCAIIBGHEY6LGDWDVRUHPRANCNFSM52GCN2HQ . You are receiving this because you were mentioned.Message ID: @.***>
Viene de acá.
Todos estos métodos están dando vuelta de alguna forma ú otra en Wollok, y todos toman un objeto y devuelven un string con alguna representación del objeto receptor
toString()
wollok-language
wollok-xtext y wollok-ts-cli
printString()
wollok-language
String
redefine agregando dobles comillas (para que en la consola un String se vea con dobles comillas). ¡pero este mensaje no es usado por el CLI! (ver abajo problemas).wollok-xtext
wollok-ts-cli
shortDescription()
wollok-language
Pero en Date es nativo:
wollok-xtext y wollok-ts
Para todos los objetos es igual que el
toString
, acá vemos la diferencia de implementación de natives.kindName()
wollok-language
toString
.wollok-xtext y wollok-ts
className()
wollok-language
wollok-xtext y wollok-ts-cli
className
es de hecho el único método de todos estos que es igual en ambos lenguajes hostProblemas:
pepita
escupesrc.arch1.pepita
y al escribirnew Golondrina()
nos devuelvesrc.arch1.Golondrina
. Nos gustaría llegar a que escupapepita
también, yun/a Golondrina
.printString
se usa en lugares medio "pobres".String>>toString
difiere de WollokXText. Entonces, lo que se muestra en la pantalla al apretar "enter" si puse un string, no tiene comillas:kindName
oclassName
que abren la puerta a preguntar por la clase en lugar de usar polimorfismo. PERO alguno de los dos es necesario, especialmente para poder escribir en un lugar genérico cómo se imprime un stacktrace (para lo cual necesito el nombre de la clase ú objeto) o para comparar excepciones en los tests.shortDescription
o cuál era la diferencia con los otros.shortDescription
(y por lo tanto detoString
) hacen cosas distintas para Date, y a fin de cuentas no hay una verdadera diferencia entreshortDescription
ytoString
.Posible solución
Propongo cambiar el lenguaje (wollok-language) de la siguiente forma:
method toString() = self.kindName()
pero podemos hacer unmethod printString() native
que se encargue de hacer que para un WKO diga su nombre, para un objeto no nombrado diga "un Objeto", y para el resto diga "un/a NombreDeClase".printString
sea lo que se espera en consola para imprimir objetos retornados, que es el redefinible. No sé en qué lugares ameritaría además reemplazartoString
porprintString
. Tunearía las documentaciones de ambos métodos para saber cuál usar y cuál redefinir en cada caso. En particular, me gusta la idea de que "toString" tenga la semántica "convertir a string" y que "printString" tenga la semántica "mostrale a une humane este objeto".className
. Que en todos los lugares donde se useclassName
se reemplace porkindName
. (me gusta más "kindName" para el nombre de un método que me dice tu identificador estático, porque podés no tener una clase, como en el caso de un WKO o un objeto no nombrado).shortDescription
(prefiero) o bien que la documentación deshortDescription
agregue algo como "e.g. for small places like object diagrams" (Y que el worksheet de wollok-run-client envíe ese mensaje para mostrar su diagrama).shortDescription
, y de hecho usakindName
, ojo si decidimos volarlo en lugar de className.