Ce qui suit est une traduction des points les plus important du README du projet que l'on peut retrouver sur https://github.com/xcombelle/chaintools. Vous pouvez jouer avec (attention, c'est en pre-0.1 donc la bibliothèque est susceptible d'évoluer
Présentation
Le but de cette bibliothèque est de créer des outils type unix avec une syntaxe pythonique. Le seul mécanisme disponible est le pipeline
Un exemple d'utilisation
chain(cat(),
filter(lambda s: s and s[0] != '#'),
map(float),
sort(),
head(n=10),
output())
Le secret d'utilisation est d'une part la fonction chain
qui fait un pipeline de générateur et les générateurs qui sont créés par des usines à générateur (que j'appelle outil). Plusieurs outils sont disponibles. Il est facile de créer sont propre outil.
Statut
Cette bibliothèque est en pre-0.1. Il y aura donc probablement des changements.
Pourquoi cette syntaxe
J'ai l'espoir d'avoir tous le pouvoir des outils unix d'une manière pythonique
Pourquoi tous les générateurs utilisent une notation de type fonction:
- une notation uniforme: pas de question sur les quels ont besoin de parenthèse ou pas
- les générateurs font quelque chose
Je crois que les usine à générateur ont des caractéristiques correspondant aux outils unix comme les options (en utilisant les paramètre) les code d'erreur (en utilisant les exception). La seule partie manquante est la sortie d'erreur standard.
La principale différence est qu'il n'y a pas un processus par outil (sauf quand on lance un programme externe):
* Il n'y a pas de parallélisation native des outils
* Il n'y a pas besoin de créer un processus pour chaque outil
Un grand avantage des chaintools est que les type ne sont pas limités à des
flux d'octet. Par exemple les outil join et split n'ont pas d'équivalent (à ma
connaissance) parmi les outils unix.
Qu'est-ce qu'on peut importer ?
On peut importer
from chaintools import (
chain,
grep,
run,
cat,
output,
split,
sort,
join,
map,
filter,
head,
tail,
null,
)
attention, cet import remplace les builtins map et filter.
# remarques sur filter et sed
Posté par goeb . Évalué à 3.
Intéressant.
Je ne sais pas si j'en aurai l'utilité, mais je fais tout de même 2 remarques.
D'une part un commande de type "sed" serait utile je pense. "sed" est globalement très utilisée.
D'autre part, noyée au milieu d'autres noms de commandes unix, "filter" est trompeur car il ne correspond pas à la commande unix "filter".
man filter
[^] # Re: remarques sur filter et sed
Posté par Xavier Combelle (site web personnel) . Évalué à 4.
Merci du commentaire
Concernant le nom des outils, c'est compliqué:
- j'ai essayé qu'ils soient le plus clair possible pour des programmeurs python
- j'ai essayé de limiter le remplacement des builtins (j'y suis pas arrivé pour map et filter)
Pour la commande sed, je vais faire une commande basée sur
re.sub
c'est suffisant à votre avis?En règle générale, si vous voyez des outils utiles dites le moi.
Toute remarque est la bienvenue.
[^] # Re: remarques sur filter et sed
Posté par Xavier Combelle (site web personnel) . Évalué à 2.
devant la demande générale j'ai ajouté un outil sub. https://github.com/xcombelle/chaintools/commit/8a88ec062706183afd4346d9100d221ec67648f1
[^] # Re: remarques sur filter et sed
Posté par Michaël (site web personnel) . Évalué à 3.
Non pas du tout sed est un petit langage de programmation à part entière, il existe depuis des décennies et a donc une quantité incroyable de scripts que les programmeurs shell utilisent. Donc si par exemple je me mettais en tête de traduire mes prototypes shell avec ta bibliothèque, remarquer qu'il n'y a pas de sed ou de awk serait rédhibitoire!
La fonction gsub est juste le niveau 0 d'utilisation de sed dans la plupart de cas, cela ne suffit absolument comme remplacement!
[^] # Re: remarques sur filter et sed
Posté par goeb . Évalué à 1.
Oui, un "sub" devrait suffire pour commencer. Il faut pouvoir :
# Autres outils
Posté par lolop (site web personnel) . Évalué à 3. Dernière modification le 27 juin 2015 à 15:38.
Je ne veux pas casser ton enthousiasme, mais il y a déjà des outils qui font ce genre de chose, et avec un syntaxe plus «shell-compliant»:
Ceci dit, ça peut être source d'inspiration ou de collaboration.
A+
Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: Autres outils
Posté par Xavier Combelle (site web personnel) . Évalué à 2.
Je connaissais pas tous ces outils, mais leur point commun est en gros d'appeler les outils shell.
Ma bibliothèque permet:
1. de faire un pipeline d'outil en pur python
2. de faciliter le développement d'outil en pur python.
Le gros avantage des outils en pur python est qu'ils sont beaucoup plus simple à développer que leur équivalent en C.
Le gros inconvénient c'est qu'il faut redévelopper les outil et qu'on ne peut pas réutiliser ceux existant. (Ceci étant minoré par le fait qu'ils sont plus simple)
[^] # Re: Autres outils
Posté par Aluminium95 . Évalué à 5.
Je ne vois pas trop l'intérêt, étant donné qu'ici c'est presque simplement de la composition de fonctions (ici de générateurs).
De plus, en python la composition est assez « peu naturelle », car tu as du code comme (ce qui se voit sur l'exemple que tu as écrit) :
En revanche, un code plus « pythonique » serait d'utiliser chaque appel comme une instruction indépendante :
Cela permet :
Alors, bien sûr, il est utile de composer directement : on peut supprimer les structures intermédiaires de données. Mais du coup, essayer de faire une notation infixe serait pas mal si tu veux inciter l'utilisation de ta fonction
chain
(même si c'est un peu un hack) :[^] # Re: Autres outils
Posté par Xavier Combelle (site web personnel) . Évalué à 2. Dernière modification le 27 juin 2015 à 15:38.
La solution "d'utiliser chaque appel comme une instruction indépendante" est la solution que j'avais déjà vu.
Elle a deux défauts à mon avis:
La solution d'utiliser une notation infixe est quasi équivalent, mais son coté hack me fait préférer une notation explicite.
[^] # Re: Autres outils
Posté par Aluminium95 . Évalué à 4.
Le côté « hack » c'est juste utiliser un symbole, comme un autre commentaire l'a montré, on peut utiliser un nom de fonction … par exemple
chain
,ce qui donne (mais qui est « moins joli »)
Après, il y a d'autres avantages à encapsuler dans un objet le calcul: tu peux contrôler les actions ! Ainsi, tu peux permettre des trucs utiles dans le genre:
Quand on y regarde de plus près, on se rend compte qu'une action (
cat
,sed
etc …) c'est simplement une fonction de typeaction A : () -> [A]
.On peut donc construire tout un tas de combinateurs :
chain
:action A -> ([A] -> action B) -> action B
qui correspond à ta versionchainEach
:action A -> (A -> action B) -> action B
qui lance un appel sur chaque ligne et concatène le résultatalter
:action A -> action A -> action A
qui essaie la première, et si elle échoue, lance la deuxième (alternative)fmap
:(A -> B) -> action A -> action B
, applique une fonction « normale » au résultat d'une actionapply
:action (A -> B) -> action A -> action B
(l'utilisation de celle-ci est plus anecdotique, mais on peut en trouver).Ensuite, on peut écrire du code comme celui-ci:
[^] # Re: Autres outils
Posté par scls19fr (site web personnel) . Évalué à 2. Dernière modification le 27 juin 2015 à 15:38.
Ce problème de composition a été soulevé par Pandas récemment
cf
http://pandas.pydata.org/pandas-docs/stable/basics.html#tablewise-function-application
with the equivalent
L'idée pourrait être reprise
[^] # Re: Autres outils
Posté par Michaël (site web personnel) . Évalué à 3.
Un intérêt est d'avoir des IO rapides à la jonction des processus puisque la communication est directement prise en charge par l'OS au lieu de tout remonter dans le langage, couper ligne à ligne, écrire immédiatement.
Sur des applications très intenses au niveau des IO, c'est une différence importante.
[^] # Re: Autres outils
Posté par Aluminium95 . Évalué à 1.
Un intérêt pour quel partie ? Faire le calcul entièrement en python ? Faire une librairie pour gérer la composition de fonctions ?
C'est la deuxième problématique que je trouvais discutable, et je n'ai pas vu/compris comment l'outil proposé améliorait l'IO en python … mis à part l'utilisation de générateurs (qui peut se faire de manière indépendante).
[^] # Re: Autres outils
Posté par lolop (site web personnel) . Évalué à 3.
Plummbum, chut et python-sh (cité dans un autre post) appellent en effet les outils du shell par derrière.
Par contre pyxshell, est un wrapper qui permet de construire des pipes en série avec la syntaxe
|
et des générateurs, le genre de truc dans lequel tu pourrais insérer tes fonctions shell-like.Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: Autres outils
Posté par Xavier Combelle (site web personnel) . Évalué à 1.
Effectivement pyxshell (et calabash dont il est dérivé) font exactement la même chose. J'aurais du chercher plus avant de me lancer dans mon truc.
[^] # Re: Autres outils
Posté par rakoo (site web personnel) . Évalué à 3.
Encore un autre: python-sh
# Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 7.
Je programme énormément en shell pour écrire des programmes que je considère comme des prototypes et comme je programme aussi en OCaml j'ai commencé à écrire Rashell une bibliothèque un peu comme celle que tu viens de commencer, mais pour OCaml.
De mon expérience de programmeur shell ce qui me semble important à considérer pour ta bibliothèque pour pouvoir écrire des programmes plus fiables que ce que permet le shell c'est:
J'ai commencé Rashell comme projet de programmation exploratrice, sans avoir un plan très clair a priori mais il me semble que l'idéal pour résoudre 1.2.3 serait de passer par des monades comme SuccessMonad qui fournissent un environnement d'évaluation aux commandes complexes.
J'ai vaguement regardé les projets qu'ont cité les autres et je n'ai pas vu comment ils traitaient 1.2.3. C'est d'ailleurs un peu une blague de citer touts ces projets pêle-mêle sans aucune hiérarchisation, certains sont beaucoup plus aboutis que d'autres. Si tu t'intéresses à la programmation système, tous ces projets sont des sources d'inspiration, Tu as peut-être des objectifs différents que ces projets là, ce qui te ferait de la place. Si au contraire il y a déjà un projet qui correspond bien à ton besoin, c'est une super nouvelle car tu as trouvé un projet que tu peux aider! :)
[^] # Re: Gestion des erreurs
Posté par Ytterbium . Évalué à 1.
Juste une petite remarque : j'apprécie tes interventions sur le shell, mais pourrais-tu ne pas mettre en gras les noms de programme (shell, sed, etc.) ? Ça casse le rythme de lecture et je pense que nous devinons tous qu'il s'agit de noms de logiciel.
[^] # Re: Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 5.
C'est l'habitude de lire et écrire des pages de man(1). :)
[^] # Re: Gestion des erreurs
Posté par Xavier Combelle (site web personnel) . Évalué à 2. Dernière modification le 28 juin 2015 à 16:38.
Par des exceptions
Qu'appelles tu informations importantes au déboguage ? Peux tu donner des exemples ?
J'ai vraiment pas compris ce que tu veux dire. D'habitude quand un script plante le plus simple c'est de le réparer et le relancer non ?
[^] # Re: Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 3.
La problématique du déboguage est la suivante: j'ai un utilitaire qui plante au milieu de la pipeline et je veux comprendre pourquoi. Je veux donc accéder à:
Si je lance la commande rm ou cp je n'ai peut-être pas besoin d'avoir accès à tous ces détails pour comprendre ce qui s'est mal passé, mais dans les cas de programmes complexes, comme par exemple un compilateur, TeX ou METAPOST, accéder à ces informations est primordial.
Pour la gestion des erreurs, imaginons que j'ai un traitement complexe à effectuer, et qu'au milieu j'ai une erreur. Par exemple j'exécute un script par SSH sur 50 serveurs et le script plante sur deux serveurs. Je peux vouloir que mon programme supervisant l'exécution des scripts me donne la chance de voir ce qui ne va pas sur les deux serveurs puis de réessayer les scripts, et si tout s'est bien passé de continuer le pipeline. Si tout ce que me propose mon programme de supervision c'est d'arrêter le traitement à la première erreur, de nettoyer à la main le bazar qu'il a laissé derrière lui et de tout reprendre depuis zéro… et bien j'aime autant programmer directement en shell.
[^] # Re: Gestion des erreurs
Posté par Aluminium95 . Évalué à 2.
On est pas vendredi, mais remplacer du shell par du caml … mis à part des améliorations purement techniques de performances, je ne comprend pas l'intérêt syntaxique (voir l'exemple qui est donné …)
Après, pour la monade, oui, c'est une solution élégante, mais en OCaml, on ne peut pas garantir la pureté des fonctions, du coup, des méthodes de type « retry » et autres pourraient avoir des comportements étranges … (si on ne fait pas attention)
Quitte à essayer de construire une bibliothèque qui le fait bien, autant coder directement en Haskell, qui possède tout ce qu'il faut pour combiner des fonctions, et qui permet (via l'évaluation paresseuse) de remplacer les générateurs de python par de simples listes … et ce sans avoir une syntaxe immonde
Pour résumer, oui l'approche monadique (ici avec
IO
) a beaucoup d'avantages :fmap
ou<$>
, ce qui permet d'utiliser toute fonction déjà écrite dans le langage que tu utilisesLe seul problème est l'intégration du code monadique dans le langage, et en python c'est pas toujours « esthétique », le problème en
caml
se pose moins puisqu'il est possible de définir des opérateurs infixes.Remarque : c'est le genre de syntaxe que j'ai proposé d'utiliser en mettant en place une classe python dans un autre commentaire.
[^] # Re: Gestion des erreurs
Posté par Xavier Combelle (site web personnel) . Évalué à 4. Dernière modification le 28 juin 2015 à 18:14.
Ça doit être mon manque de connaissance en Haskell, le caractère lisible et facile à comprendre ne me saute pas aux yeux. (NB il manque le filter dans le dernier exemple mais c'est tout sauf grave)
Prenons le dernier exemple qui me parait le plus simple (si on omet l'explication de la fonction cat):
.
doit être pour la composition de fonction()
à la fin ?Les point 2 et 4 me paraissent tout à fait inexplicable.
Ce que je veux dire c'est que votre familiarité avec un langage fait croire que ce langage est clair, mais qu'en fait ce n'est le cas que pour ceux qui sont familiarisés avec ses idiomes.
PS1: Si quelqu'un veut bien m'expliquer les points mystérieux il est le bien venu
PS2: J'ai toujours voulu apprendre le Haskell sans jamais y arriver, quelqu'un peut-il m'indiquer un ou des points de départs ?
[^] # Re: Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 2.
Voilà un exemple de programme pas très clair: je veux afficher
Hello, world!
et je dois écrire:Par rapport à mon but il y a plein de bruit que je ne comprends pas. Je vois bien que le biscuit c'est le
printf(…)
mais quel est le rapport entre mon but et lemain
et les#include
,argc
,argv
? Ce n'est pas clair parceque beaucoup d'éléments sont sans lien apparent avec mon but. Quand tu lison retrouve un peu de bruit
main
etoutput
mais la plupart des éléments peuvent facilement être rattachés à la description de la tâche. Comme dans ton exempleIl y a un petit peu de bruit, le
chain
mais les autres éléments sont facilement rattachables à la description de la tâche à effectuer.[^] # Re: Gestion des erreurs
Posté par Aluminium95 . Évalué à 2.
Euh, c'est moi qui ai oublié de le remplacer par
(\s -> length s > 0 && (s !! 0) /= '#')
, du coup c'est normal que cela n'ai pas de sens …En fait, c'est « un peu plus » que cela. Imaginons que tu te donnes un type
A
quelconque, et que tu puisse manipuler des types[A]
(ici c'est liste, mais en fait, ça peut être « programme »,IO
…). Tu te dis :A
[]
(indépendantes deA
, par exemplehead
,tail
etc …)A -> B
en une fonction de[A] -> [B]
: c'estfmap
qui le faitAlors, pour les listes,
fmap = map
. Mais en fait c'est beaucoup plus général : du moment que tu as un type paramétré, tu peux espérer trouver une fonctionfmap
, et qui a le bon goût de vérifier quelques axiomes (fmap id = id
oufmap f . fmap g = fmap (f . g)
etc …)Donc en fait
fmap
, c'est « functor map », et c'est un moyen de transformer une fonction « normale » sur une fonction d'un type paramétré.Dans le cas présent, c'est
IO a
qui est un type paramétré, et qui représente un calcul effectué dans un état global.Exactement, et cela vient très naturellement. Bon, c'est cool de pouvoir appliquer les fonctions normales sur ton type, mais tu as construit un type, c'est pas pour rien : il faut pouvoir faire plus de choses. En plus, tu as souvent des fonctions comme :
a -> F b
(j'ai remplacé[]
parF
) et le problème … c'est qu'elles ne se composent pas ! C'est tout ce que>>=
fait : donner un moyen de composer ce nouveau type de fonctions.f : a -> F b
: prend un type normal, retourne un type complexeg : b -> F c
: idemf >=> g : a -> F c
: prend un type normal, retourne un type complexe (en effectuantf
puisg
)En fait, pour des raisons pratiques, on utilise plus souvent
>>=
que>=>
, aveca >>= g === (\_ -> a) >>= g
.Exemple pratique, tu récupères la liste des arguments de la ligne de commande, c'est une action, de type
Action [String]
, tu veux afficher cette liste, c'est une action, mais elle attend la chose à afficher en argument :Donc en réalité, tout le code qui est écrit en exemple est soit
IO
)En espérant que mes explications sont claires :-)
[^] # Re: Gestion des erreurs
Posté par max22 . Évalué à 2.
Perso j'ai commencé avec le bouquin "Programming in Haskell" de Graham Hutton. J'ai aussi fait le tutorial Learn yourself a Haskell for great good. Traduction en français ici.
Sinon il y a le livre "Real World Haskell", je l'ai depuis plusieurs années, il prend un peu la poussière malheureusement. Bien qu'il ait l'air intéressant. (j'ai juste lu le début)
Au début, on est tenté de faire seulement des petits trucs, parce que sans boucles et sans effets de bord on est un peu perdu. Moi aussi j'ai tenté puis abandonné, puis re-tenté, puis re abandonné, et là je suis en train d'essayer de coder un gros truc avec, je sens que ça vient.
[^] # Re: Gestion des erreurs
Posté par Sébastien Burton . Évalué à 2. Dernière modification le 29 juin 2015 à 11:45.
Sur stackoverflow, une personne a publié un parcours d'apprentissage du Haskell composé de diverses ressources accessibles en ligne (il s'appuie notamment sur deux livres que tu cites): Getting started with Haskell. J'ai trouvé cette approche vraiment enrichissante et j'en conseille la lecture à quiconque souhaiterait apprendre le Haskell, ne serait-ce que pour prendre un peu de recul. En passant, d'autres livres ont suivi sur le sujet chez O'Reilly.
[^] # Re: Gestion des erreurs
Posté par Xavier Combelle (site web personnel) . Évalué à 1.
en passant sur #haskell, l'auteur de https://github.com/bitemyapp/learnhaskell/blob/master/guide-fr.md me l'a conseillé j'ai commencé par Le cours cis194 de Yorgey et il a l'air vraiment bien
[^] # Re: Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 3.
Le but est d'aboutir à une meilleur gestion des erreurs. Le shell est très efficace pour écrire des prototypes. Dans mon poste actuel, j'ai codé un environnement de test unitaires, des outils d'intégration continue et de déploiement, des outils de maintenance de serveur, de configuration de serveur, … en quelques semaines à peine, en shell.
Mais ces programmes ne sont que des prototypes, qui mériteraient des versions plus robustes.
Pour l'approche monadique je pensais plutôt à l'autre côté: la description de la tâche et sa supervision plutôt que l'IO. Ce que je vais essayer dans les prochains jours c'est de programmer une monade dans laquelle on peut définir une commande complexe et faire tout le pipe-fitting qu'on veut, définir les options de supervision, etc.
Quand on crée et efface des fichiers, qu'on compile des programmes ou qu'on installe des paquets sur un serveur, on est intéressé essentiellement par les effets de bord, et aucun langage ne va magiquement rendre le retry facile à implémenter!
[^] # Re: Gestion des erreurs
Posté par Michaël (site web personnel) . Évalué à 4.
On n'est pas vendredi, mais remplacer du caml par du haskell ;)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.