Utilisation sympa des méthodes d'extensions

108 views
Skip to first unread message

mathias kluba

unread,
Jul 4, 2012, 11:29:03 AM7/4/12
to paris...@googlegroups.com
Hello!

Est-ce que quelque chose de similaire existe en .Net? https://vimeo.com/39819321
En gros, ça utilise les méthodes d'extensions pour construire une "distance" à partir d'un Integer.
Ex:
1.mm + 3.cm - 2.km
Ca serait aussi sympa d'avoir ça pour le temps:
1.sec + 3.min - 4.h

Rui Carvalho

unread,
Jul 4, 2012, 12:06:28 PM7/4/12
to paris...@googlegroups.com
Il n'y a rien de bien officiel à ma connaissance malgré de nombres options de librairies de traitement des unités qui vadrouillent à droite à gauche...

C# n'étant pas tout à fait un langage extensible au niveau de sa structure, on ne peut pas lui coller des membres sur des nombres comme ça, par contre à coup d'extension methods, oui, tu peux faire quelque chose de ressemblant:

var distance = 1.mm() + 3.cm() + 2.5.Km();
if (distance == 2500.031) Console.WriteLine("{0} mètres",distance);
que tu as en vrac avec une classe à l'arrache:
public static class IntTimeExtensions
{
public static double mm(this double current)
{
return current * 0.001;
}
public static double cm(this double current)
{
return current * 0.01;
}
public static double m(this double current)
{
return current;
}
public static double Km(this double current)
{
return current * 1000;
}
public static double mm(this int current)
{
return current * 0.001;
}
public static double cm(this int current)
{
return current * 0.01;
}
public static double m(this int current)
{
return current;
}
public static double Km(this int current)
{
return current * 1000;
}
}

Idéalement au lieu de retourner un double il faudrait retourner directement une unité typee compatible double dans lequel on aurait setter au passage l'unité en cours.

Après on peut imaginer plein de trucs sympa à base de lambdas et autres choses mais dans tous les cas tu ne pourras pas y échapper si tu essaye de faire autre chose que des unités à dimension simple, il faut être capable d'analyser l'arbre produit par le calcul complet et là c'est une autre paire de manches...

my 2 cents

Rui.




2012/7/4 mathias kluba <mathia...@gmail.com>

Olivier Azeau

unread,
Jul 4, 2012, 12:27:51 PM7/4/12
to paris...@googlegroups.com
J'avais écrit un billet sur ce genre d'utilisation des méthodes
d'extension :
http://agilitateur.azeau.com/post/2010/10/12/C-et-Ruby%2C-des-syntaxes-si-diff%C3%A9rentes

Olivier

Yann Schwartz

unread,
Jul 4, 2012, 1:29:33 PM7/4/12
to paris...@googlegroups.com
Il y a un langage spécialisé très marrant qui se spécialise dans ce genre de choses : Frink (  http://futureboy.us/frinkdocs/#HowFrinkIsDifferent  ) qui compile sur la JVM.

Et bien sûr, un portage vers Clojure appelé Frinj et qui illustre la facilité de définir des DSL en Lisp :


qui reprend le tutorial de Frink, mais en clojure.



2012/7/4 Rui Carvalho <r...@rui.fr>

Yann Schwartz

unread,
Jul 4, 2012, 1:30:37 PM7/4/12
to paris...@googlegroups.com
On pourrait aussi citer les units of measure de F#



2012/7/4 Yann Schwartz <abolib...@gmail.com>

Rui Carvalho

unread,
Jul 4, 2012, 7:57:54 PM7/4/12
to paris...@googlegroups.com
rhaaa, voila c'était ds F#, que j'avais apperçu les units, je me disais bien qu'il devait y avoir qqch comme ça ds .net. Je me demande si il n'y avait pas une lib de ms labs pour .net en général qui s'occupait de ça?

Juste pour revenir sur les méthodes d'ext, même si cela peut sembler pratique au premier abord (comme toujours avec ces dernières), je ne suis pas sur que l'on puisse aller très loin avec dans tous les cas.

J'ai un peu cherché la manière la moins crade en termes de fonctionnalités et d'écriture (je m'amuse d'un rien faut dire, merci Mathias ;-), et j'en suis arrivé à utiliser les cast pour ça...voila ce a quoi j'arrive:

var a = (Meter)123 + (Kilometer)2.3 + (Centimeter)1234;
Console.WriteLine(a.ToString());
Console.WriteLine(a.To<Kilometer>().ToString());
Console.WriteLine(a.To<Centimeter>().ToString());

var b = (Meter)2 * (Centimeter)30;
Console.WriteLine(b.ToString());
Console.WriteLine(b.ToSurface<Centimeter>().ToString());

/*
Prints:

2435.34 Meters
2.43534 Kilometers
243534 Centimeters
0.6 Squared Meter
6000 Squared Centimeters

*/

C'est en mode poc, donc pas clean, et c'est dans un gist pour qu'on puisse discuter dessus si il y en a que ça intéresse.

Sinon je serai intéressé de voir toute implem de cette problématique.

Voici le gist avec le code:

https://gist.github.com/4dafaa0252944815f16c


Rui


#  =================================================
#  Blog (fr)   ›› http://www.rui.fr 
#  Blog (en) ›› http://www.codedistillers.com/  
#  Twitter     ›› @rhwy
#  LinkedIn  ›› http://fr.linkedin.com/in/ruifr
#
#  =================================================



2012/7/4 Yann Schwartz <abolib...@gmail.com>

Cédric Rup

unread,
Jul 5, 2012, 1:30:40 AM7/5/12
to paris...@googlegroups.com
Visiblement, dans F# les Units of Measure ne sont pas conservées après compilation... donc il faut aller voir ailleurs ;o)

Cédric

2012/7/5 Rui Carvalho <r...@rui.fr>

Mathias Kluba

unread,
Jul 5, 2012, 1:35:18 AM7/5/12
to paris...@googlegroups.com
j'aime bien ta façon de faire avec les casts implicites :)
mais si j'ai le temps je testerais bien avec les méthodes d'extensions...

--
Mathias Kluba
Twitter: @mathiaskluba
Blog: http://grozeille.com

Jérôme Avoustin

unread,
Jul 5, 2012, 3:26:56 AM7/5/12
to paris...@googlegroups.com
Tiens Rui

Petite proposition d'amélioration pour les prochains exemples de codes :)
Au lieu des Console.Writeline, un petit Assert dans une méthode décorée avec [Test] :)

D'autant que je ne m'imagine pas une seconde traiter ce genre de problèmes sans TDD... !  :)

@+


2012/7/5 Rui Carvalho <r...@rui.fr>

Simon Mourier

unread,
Jul 5, 2012, 3:43:30 AM7/5/12
to paris...@googlegroups.com
Il y a un projet "NGenericDimensioons" qui a l'air de ressembler à ça:  http://ngenericdimensions.codeplex.com/ 



2012/7/4 mathias kluba <mathia...@gmail.com>

Rui Carvalho

unread,
Jul 5, 2012, 5:06:22 AM7/5/12
to paris...@googlegroups.com
@jerome,

oui c'était le but de ces write mais pour ce genre de poc, je fais ça en live sur linqpad qui est tres bien pour faire ce genre de tests
et du coup tu fais du tdd implicite sans framework de test mais ce n'est pas grave car tu as ton feedback en live ;-)

@mathias,
comme je le disais, j'ai fait un petit test avec les méthodes d'extension, mais je ne suis pas convaincu par l'approche dans la mesure où elles ne sont qu'un artifice pour se greffer sur les nombres et où l'on aurait certainement mieux fait d'encapsuler les nombres dans des vraies classes. Est-ce que faire 'Distance.Meter(123) + Distance.Kilometer(2.4)' est beaucoup plus contraignant que de faire '123.Distance<Meter>() + 2.5.Distance<Kilometer>()' ou un truc du genre?

De plus encore une fois cela ne te permet pas d'agir au niveau de l'expression complete mais juste de transformer un nombre en quelque chose de plus riche avec une unité. Si au lieu d'additionner, tu multiplie cela devient quoi? 
reprend ça '123.Distance<Meter>() * 2.5.Distance<Kilometer>()' mais avec la multiplication cette fois ci, comment tu récupère un résultat sous forme de surface?

C'est tout l'intérêt de jouer avec la redéfinition des casts et des opérations, tu peut agir sur des membres deux a deux au niveau des opérations (sans passer par de la manipulation d'expressions. Dans mon exemple quand je fais '(Meter)2 * (Centimeter)30;'je récupère bien un type surface de 0,6 M^2. L'autre avantage aussi c'est que tu peux vérifier à la compil que tu ne fais pas d'opérations sur les choux et les carottes!


Rui.

#  =================================================
#
#  Blog (fr)   ›› http://www.rui.fr 
#  Blog (en) ›› http://www.codedistillers.com/  
#  Twitter     ›› @rhwy
#  LinkedIn  ›› http://fr.linkedin.com/in/ruifr
#
#  =================================================




2012/7/5 Simon Mourier <simon....@gmail.com>

Olivier Azeau

unread,
Jul 5, 2012, 5:58:38 AM7/5/12
to paris...@googlegroups.com
On 05/07/2012 11:06, Rui Carvalho wrote:

> De plus encore une fois cela ne te permet pas d'agir au niveau de
> l'expression complete mais juste de transformer un nombre en quelque
> chose de plus riche avec une unité. Si au lieu d'additionner, tu
> multiplie cela devient quoi?
> reprend ça '123.Distance<Meter>() * 2.5.Distance<Kilometer>()' mais avec
> la multiplication cette fois ci, comment tu récupère un résultat sous
> forme de surface?
>
> C'est tout l'intérêt de jouer avec la redéfinition des casts et des
> opérations, tu peut agir sur des membres deux a deux au niveau des
> opérations (sans passer par de la manipulation d'expressions. Dans mon
> exemple quand je fais '(Meter)2*(Centimeter)30;'je récupère bien un type
> surface de 0,6 M^2. L'autre avantage aussi c'est que tu peux vérifier à
> la compil que tu ne fais pas d'opérations sur les choux et les carottes!
>

Une idée pour passer par les extension methods sans cast et avoir les
surfaces en multipliant des distances :
https://gist.github.com/3052728


Olivier

Rui Carvalho

unread,
Jul 5, 2012, 6:08:01 AM7/5/12
to paris...@googlegroups.com
c'est cool Olivier, moi j'aime bien ;-)

on a a peu de choses près la meme implémentation, ce qui compte c'est 1) de définir des types de base pour définir sur quel type de mesure on est et 2) redéfinir les opérateurs pour les associations.

dans ton implem, je rajouterai peut être un enum avec l'unité de mesure dans le constructeur pour que l'on aie une trace de la mesure initiale (genre un DistanceUnits.Meters), un peu comme le T de la timezone dans les dates.

Rui.

#  =================================================
#
#  Blog (fr)   ›› http://www.rui.fr 
#  Blog (en) ›› http://www.codedistillers.com/  
#  Twitter     ›› @rhwy
#  LinkedIn  ›› http://fr.linkedin.com/in/ruifr
#
#  =================================================




2012/7/5 Olivier Azeau <olivie...@gmail.com>

Jb Evain

unread,
Jul 5, 2012, 8:43:49 AM7/5/12
to paris...@googlegroups.com
2012/7/5 Cédric Rup <cedri...@gmail.com>:
> Visiblement, dans F# les Units of Measure ne sont pas conservées après
> compilation... donc il faut aller voir ailleurs ;o)

Elles le sont.
Seulement elles ne sont accessibles que depuis F#.
Avec F# 2 tu as un module SI dans le powerpack, dans F# 3 c'est
directement dans le runtime F# il me semble.

Exemple:

```
open SI

let distance = 2<m>
```

Jb

Jb Evain

unread,
Jul 5, 2012, 9:12:02 AM7/5/12
to paris...@googlegroups.com
Je pensais que Rob allait répondre, mais comme il ne le fait pas, je
vais faire le F#ista intérim.

Le système d'unité de F# est super chouette, mais il n'est pas
vraiment fait pour pour représenter des simples multiples de la même
unité.
Là où c'est fort on sait toujours dans quelle unité on s'exprime. Si
on regarde l'exemple d'un pseudo langage (non pas que scala est un
pseudo langage, hein):

2.cm + 4.m

On ne sait pas quelle est l'unité de sortie. Ok c'est une distance,
mais exprimée en quoi ? En centimètres ? En mètres ? Il va falloir
décider nous même lors de son utilisation, en fait.
On peut faire un parallèle avec le typage : c'est un peu comme si on
passait des “object” de partout, et qu'on castait à l'usage. Du coup
on peut facilement faire des conneries.

F# force statiquement à répondre à cette question (faut dire c'est un
peu son créneau).

Si on veut passer dans un multiple, il va falloir définir nous même
les conversions, ça donne une syntaxe un peu moche, et verbeuse:

[<Measure>]
type m =
static member to_km(m) = m * 0.0001<km/m>
and [<Measure>] km =
static member to_m(km) = km * 1000.0<m/km>
and [<Measure>] cm =
static member to_m(cm) = cm * 0.001<m/cm>
and [<Measure>] mm =
static member to_m(mm) = mm * 0.0001<m/mm>

Du coup on peut écrire:

let d = mm.to_m(1.0<mm>) + cm.to_m(3.0<cm>) + km.to_m(2.0<km>)

Au moins on sait que d est de type float<m>

F# ne nous laisse pas définir des méthodes d'extensions sur des
unités, du coup c'est dur d'avoir un truc fluent.

Voilà.

Jb

2012/7/4 Yann Schwartz <abolib...@gmail.com>:

Rui Carvalho

unread,
Jul 5, 2012, 9:18:25 AM7/5/12
to paris...@googlegroups.com
donc au final, ce que je peux en comprendre de tes exemples Jb, en F# ce n'est pas forcément plus sexy que ce que l'on peut faire en C#...


Rui.

#  =================================================
#  Blog (fr)   ›› http://www.rui.fr 
#  Blog (en) ›› http://www.codedistillers.com/  
#  Twitter     ›› @rhwy
#  LinkedIn  ›› http://fr.linkedin.com/in/ruifr
#
#  =================================================




2012/7/5 Jb Evain <jbe...@gmail.com>

Yann Schwartz

unread,
Jul 5, 2012, 9:24:46 AM7/5/12
to paris...@googlegroups.com
Sexy, je sais pas, mais les unités de mesure F# assurent que la dimension de ta valeur finale est correcte (tu n'additionnes pas de N/m² et des Pa par exemple), ce qui est terriblement utile dans les calculs physiques. Par contre, comme le dit jb, ça ne gère pas les multiples d'une même unité.



2012/7/5 Rui Carvalho <r...@rui.fr>

Jb Evain

unread,
Jul 5, 2012, 10:20:02 AM7/5/12
to paris...@googlegroups.com
Probablement qu'ils utilisaient des méthodes d'extension “sexy”:

http://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure

;)

2012/7/5 Yann Schwartz <abolib...@gmail.com>:

Rui Carvalho

unread,
Jul 5, 2012, 10:32:53 AM7/5/12
to paris...@googlegroups.com
yep, on a pas idée d'utiliser des "pound-force" au lieu d'un bon vieux Newton aussi ...

Rui.

#  =================================================
#
#  Mob        ›› +33610047040

Jérôme Avoustin

unread,
Jul 5, 2012, 10:34:39 AM7/5/12
to paris...@googlegroups.com
Comme on n'a pas idée de rouler à gauche... :)


2012/7/5 Rui Carvalho <r...@rui.fr>

Vincent Bourdon

unread,
Jul 5, 2012, 2:15:40 PM7/5/12
to paris...@googlegroups.com
Le choc, enfin l'atterrissage à du être sexy aussi :)

Vincent B.
Reply all
Reply to author
Forward
0 new messages