Blabla@Scala - Langages fonctionnels - Programmation
Marsh Posté le 14-12-2020 à 00:56:29
Question du Riton, pourquoi utiliser Tapir, ici avec http4s.
Petit exemple, une API qui prend un code postal suisse en paramètre, qui ont la propriété d'être compris entre 1000 et 9999, ça va être plus simple qu'un code postal français pour la démo.
Un value class pour emballer l'entier qui représente le code postal, et son codec en partant du décodeur d'entier fourni par Tapir:
Code :
|
Gestion custom des erreurs pour leur attacher un statut HTTP:
Code :
|
Les extracteurs pour les requêtes, un path param, un query param qui accepte une liste non-vide de codes postaux:
Code :
|
Les routes:
Code :
|
La doc OpenAPI autogénérée:
openapi: 3.0.3 |
Et il n'y a plus qu'à connecter les endpoints à la logique qui appelle un service ou une BDD qui retourne un IO[Either[CustomError, Response]] par exemple.
Marsh Posté le 14-12-2020 à 08:51:19
C'est top, merci !
Je vous tiendrais au jus des choix et difficultés
Marsh Posté le 14-12-2020 à 20:11:38
DDT a écrit : |
J'ai un peu de mal sur le rôle du Codec, notamment comment il complète la partie serialization. Le Codec se suppléé au Decoder Circe ? Ou il le remplace ? Je comprend bien que le rôle du Codec est plus large (validation, transfo), mais est-ce qu'il descend aussi bas ?
La serialization est un vrai sujet dans mon cas, on doit bosser avec des classes Java très complexes (bean avec un max d'héritage) et on aimerait autant que possible renforcer les I/O de l'API en fonction de ces beans.
Marsh Posté le 14-12-2020 à 21:44:35
Les codecs de Tapir sont nécessaires à transformer les entrées/sorties de tes endpoints; la sérialisation en JSON des entités en entrée et/ou sortie de tes requêtes et réponses n'est qu'une des combinaisons possibles. Dans certains cas Tapir le fait directement, dans d'autres comme le JSON, tu as besoin d'une autre bibliothèque.
Ici par exemple, le binding entre les deux codecs est la méthode jsonBody, dans le module tapir-json-circe (si tu veux utiliser Circe).
Dans mon code plus haut je veux construire deux endpoints qui extraient un ou plusieurs codes postaux directement depuis l'URL de la requête.
Donc on a String -> Int qui est déjà fourni par Codec.int, puis Int -> PostalCode que je construis moi-même.
Dans l'autre sens, si je voulais utiliser mes endpoints avec le client STTP par exemple, le codec permet de faire PostalCode -> String pour construire la requête.
Avec le Codec attaché aux E/S de ton endpoint, tu as une instance optionnelle de Validator et aussi de Schema qui est dérivé de manière automatique.
Mais tu peux décrire tes schémas explicitement si tu n'es pas satisfait du YAML généré. J'ai pas de Java, mais j'ai des hiérarchies parfois un peu trop complexes pour la dérivation automatique.
Marsh Posté le 21-12-2020 à 14:49:10
Tu utilises ce pattern pour l'implémentation Tapir <=> http4s ?
https://github.com/hejfelix/tapir-h [...] tion.scala
C'est franchement aride pour qui n'a pas 10 ans de cats derrière lui, ça tire énormément de concepts, et le fait que ça dépende d'explicits à foison n'arrange rien.
Marsh Posté le 21-12-2020 à 16:07:49
Oui je fais des choses similaires.
Note qu'ici y a plein de choses dans la même classe, en vrai tu pourrais simplifier pas mal
- La route pour la doc et Swagger UI est déjà fournie par le module swagger-ui-http4s, pas besoin d'aller mélanger le DSL à ton API.
- T'es pas obligé d'utiliser un F générique et tagless final, même si c'est une bonne pratique. Utiliser IO concret partout va réduire le nombre d'implicites.
- ... voire considérer BIO si tu te retrouves à gérer beaucoup de IO[Either[Throwable, R]]
- Le reduceK pour combiner toutes les routes d'un coup c'est malin (je devrais faire ça tiens ) mais tu peux très bien combiner les routes une à une comme dans les exemples moins compliqués.
- Tous les F.delay ici c'est pour illustrer les effets de bord, en pratique tu devrais déjà recevoir un IO quand tu connectes les routes à ta logique (par exemple si tu appelles doobie).
Marsh Posté le 07-01-2021 à 16:25:43
DDT a écrit : |
C'est typiquement l'exemple où sans la compréhension de F[_] / cats / FP / whatever je butte et je ne trouve rien de pertinent.
C'est sans doute simple, mais comment tu combines plusieurs HttpRoutes[IO] (pas générique) ? J'ai ni reduceK ni <+> a dispo ou alors je vois pas comment.
Il y a une barrière à l'entrée assez haute pour qui ne maîtrise pas le FP a utiliser ces outils, on parle pourtant juste de dev une API.
Marsh Posté le 07-01-2021 à 23:06:05
Oui t'as raison pour <+> (combineK) il faut importer les méthodes d'extension pour SemigroupK
Code :
|
Tu peux toujours partir avec un:
Code :
|
voire en dernier recours:
Code :
|
s'il te manque aussi l'instance de la typeclass, puis essayer de raffiner tes imports ensuite.
Pour celui-là, faut le savoir, ou aller voir un exemple dans la doc, c'est vrai.
Pour le reste, si je vois ce que j'utilise dans ma codebase, c'est surtout des map/flatMap/sequence/traverse, exactement comme je le ferais avec des Future, et 2-3 trucs qui viennent assez naturellement, comme *> (productR) dans Apply qui est très pratique. Et là si tu utilises directement IO, tu n'auras pas besoin d'imports.
Je te conseille https://www.scala-exercises.org/cats/
C'est vite fait et ça devrait suffire comme introduction. Il n'y a pas besoin de connaître Cats de bout en bout pour utiliser http4s confortablement. Par exemple HttpRoutes est simplement un type alias qui utilise un Kleisli, mais t'as pas vraiment besoin de le savoir.
Et le livre d'Underscore si tu veux approfondir ou juste avoir une référence sous le coude: https://www.scalawithcats.com/
Et pourquoi s'embêter avec tout ça finalement? De mon expérience c'est dur d'anticiper le moment ou tu vas faire face à une limitation du framework.
Et là si je dois faire un truc un peu plus bas niveau, c'est vraiment appréciable de pouvoir utiliser Cats Effect (Resource est vraiment génial par exemple) ou fs2 directement, de manière parfaitement intégrée au reste.
Marsh Posté le 11-01-2021 à 17:47:20
DDT a écrit : Oui t'as raison pour <+> (combineK) il faut importer les méthodes d'extension pour SemigroupK
|
C'est bien ce que j'ai essayé, mais j'arrive à rien même avec un exemple minimaliste :
Code :
|
J'importe la terre entière, il n'y a strictement aucune code et j'obtiens ça :
value <+> is not a member of org.http4s.HttpRoutes[cats.effect.IO] |
je n'ai évidemment pas possibilité d'appeler combineK sur l'objet non plus.
Je suis en Scala 2.12, j'ai bien -Ypartial-unification dans les options de compilation.
Franchement je sèche. Je passe à côté d'une évidence ?
DDT a écrit : |
J'étais tombé sur le livre d'Underscore, il est dans ma todo. Je vais commencer par scala-exercices/cats
Merci encore pour ton aide !
Marsh Posté le 13-01-2021 à 00:22:34
|
Ça doit compiler.
- Les imports faut choisir: soit tu importes uniquement les méthodes d'extension pour SemigroupK, soit toutes (cats.syntax.all), soit tous les implicites (qui correspond à syntax.all et instances.all). Si tu importes plusieurs fois les mêmes implicites ça compile pas.
- Le trait Http4sDsl tu peux l'utiliser comme mix-in, c'est plus propre.
Mais sinon ça devrait marcher.
Pour les paramètres de scalac j'utilise https://github.com/DavidGregory084/sbt-tpolecat
Ça force à garder le code relativement propre.
Marsh Posté le 13-01-2021 à 08:45:55
Haha, un énorme merci tu m'as aidé à trouver le problème
DDT a écrit :
|
Bien sûr, j'ai mis la totale dans mon exemple pour montrer ce que j'avais essayé
C'était bien un problème de compilateur et pas d'imports au final. Important pour la suite : j'utilise Maven (because reasons) sur Intellij, pas sbt.
Le `-Ypartial-unification` était dans les settings scalac de maven, pas ceux de l'IDE.
Ton exemple minimaliste et l'assurance que ça devait compiler m'ont aidé à isoler le problème, merci beaucoup. C'est ce qui est compliqué en débutant sur un truc très riche, j'ai tendance à remettre en question mon code plutôt que le setup.
Edit : bon ça build dans l'IDE mais c'est toujours rouge dans le code. Au moins le problème est isolé non rien
Marsh Posté le 11-02-2021 à 10:39:52
Je sens que c'est pas très idiomatique, mais est-ce qu'il y a un moyen d'utiliser un type paramétré sur la définition d'un endpoint ?
Code :
|
le code lève une erreur sur 'jsonBody' du fait de l'absence de decoder et encoder implicit pour T.
Code :
|
n'aide pas :
could not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoder[T] |
Marsh Posté le 11-02-2021 à 11:00:23
Y a pas de raisons que tu puisses pas mais du coup il te faut
Code :
|
ou
Code :
|
selon où tu vas appeler cette méthode.
Mais tu vas vraiment connecter l'endpoint à un service qui peut te retourner n'importe quel type en sortie?
Car là tu ne fais que déplacer la contrainte sur le polymorphisme de T: il te faut forcément une instance d'Encoder lorsque tu appelles cette méthode, donc ça veut dire que tu sais ce qu'est T à un endroit ou un autre, non?
Marsh Posté le 11-02-2021 à 11:49:33
Chez moi ça compile pas, les encoders/decodes/schemas ne sont pas accessibles.
Code :
|
que je veux utiliser ensuite de cette manière
Code :
|
Et oui je comprend ta remarque, je pense que je passe à côté de quelque chose. Dans mon code tous les sous types de MyTrait sont des case class, mais le compilo n'a pas moyen de le savoir.
Je voudrais donner un contrat simple disant que je veux des endpoints typés avec la garantie que le type est une case class et pour lequel je m'attend donc à ce que jsonBody[T] sache décoder/encoder sans surcoût.
Les case classes correspondantes ont toutes une logique métier commune qui est détaillée dans MyTrait et pour laquelle j'ai également un service paramétré.
Je pense qu'il y a moyen de faire autrement, mais j'aimerais avant d'exposer ça de manière détaillée expérimenter jusqu'au bout cette première implem, et je ne butte que sur le jsonBody.
Marsh Posté le 11-02-2021 à 12:21:20
Dans ce cas
Code :
|
Si MyTrait est sealed tu peux utiliser la dérivation (semi)automatique de Circe.
Si c'est pas le cas, tu définis tes codecs à la main et tu gères toutes les case classes.
Par exemple dans un trait MyTraitCodecs comme mix-in
Code :
|
En gros 1) avoir les codecs quelque part 2) les passer à jsonBody: soit implicitement, soit par un import, soit mixed in.
Marsh Posté le 11-02-2021 à 13:42:06
DDT a écrit : Si MyTrait est sealed tu peux utiliser la dérivation (semi)automatique de Circe. |
Tu peux compléter ce point particulier ?
Code :
|
|
ou je comprend mal et il faut tout de même déclarer les implicits dans chacune des case class ?
Comme :
Code :
|
Marsh Posté le 12-02-2021 à 00:58:25
Y a plusieurs stratégies pour dériver les codecs d'un ADT, mais oui tu peux appeler deriveEncoder/deriveDecoder (ou deriveCodec tant qu'à faire) dans chaque objet companion de tes case classes.
Ici avec la dérivation auto ça marche en tout cas.
Code :
|
Marsh Posté le 26-03-2021 à 13:54:40
Vous utilisez quoi comme formateur / règles de formatage de code, des imports ?
scalafmt est ce qui revient le plus souvent mais j'aime pas le style (:o) et j'aimerais pour autant ne pas avoir à créer de règles customs. Il y a un standard admis par la communauté ? Si le tooling suit, c'est bonus.
Merci !
Marsh Posté le 26-03-2021 à 15:11:47
J'utilise scalafmt, c'est un truc que j'ai introduit dans toutes mes équipes dès qu'Olaf l'a présenté publiquement.
Y a plusieurs presets pour le style, t'as essayé quoi?
IntelliJ détecte le fichier de configuration et l'utilise par défaut depuis un moment déjà.
J'ai ajouté un check dans notre CI/CD.
Pour les imports on se contente de l'optimisation des imports par IntelliJ. L'ordre n'est pas vérifié mais si tu as "-Wunused:imports" au moins tu ne rates pas les imports inutiles.
Pour paramétrer scalac de manière stricte cf. https://github.com/DavidGregory084/sbt-tpolecat
Marsh Posté le 26-03-2021 à 15:32:48
Je ne suis pas allé plus loin que le preset par défaut. Ce qui m'irrite dans scalafmt, c'est le parti-pris du line break pour les paramètres de classes et arguments de méthode.
J'aime que le line break n'intervienne que sur mon max column, je préfère avoir plus de code sous les yeux sur une même hauteur d'écran.
Alors oui j'imagine que ça se configure, mais je suis plutôt partisan d'adhérer en bloc (à la limite à un preset de haut niveau alternatif) plutôt que de maintenir ses propres règles.
Mais si tout le monde aime scalafmt et le default preset, je ferais avec
Edit : en se basant sur mon rant et si j'ai bien compris, le preset Intellij devrait faire le job.
Tu (vous ?) utilisez quel preset si scalafmt ?
Marsh Posté le 27-03-2021 à 14:13:02
ReplyMarsh Posté le 14-04-2021 à 12:12:49
Vous utilisez des best practices / coding conventions / coding styles particuliers, et idéalement documentés et publics ?
Le langage permet de décliner sur plusieurs paradigmes et j'aimerais m'appuyer sur des références pour homogénéiser notre base de code et les reviews.
Marsh Posté le 16-04-2021 à 10:53:24
Je dirige les débutants vers https://nrinaudo.github.io/scala-best-practices/ (Nicolas a présenté le contenu à plusieurs conférences, y a même une présentation en français à Scala IO).
En plus de Scalafmt, j'aime pas Scalastyle mais on utilise Scapegoat et scalac configuré avec les flags activés par sbt-tpolecat.
T'utilises Maven et scala 2.12?
Code :
|
Pour les faux positifs, y a l'annotation @nowarn qui a été backportée dans Scala 2.12.13.
Marsh Posté le 17-04-2021 à 19:30:52
Wartremover est populaire également, peut-être plus que Scapegoat. Mais son fonctionnement semble plus primitif.
Marsh Posté le 21-04-2021 à 08:22:23
DDT a écrit : Je dirige les débutants vers https://nrinaudo.github.io/scala-best-practices/ (Nicolas a présenté le contenu à plusieurs conférences, y a même une présentation en français à Scala IO). |
C'est pas exhaustif mais c'est une bonne première base
Certains point un peu à contre courant de mon intuition, c'est intéressant, à s'approprier.
DDT a écrit : |
Top merci !
Pour le moment on est sur Sonar pour des raisons historiques, autant dire peu d'utilité pour Scala. Je vais regarder dans le détail.
Oui pour les flags de compil, cette liste était déjà dans ma todo
Encore merci pour tes conseils
Marsh Posté le 21-04-2021 à 10:20:48
Par curiosité quels sont les points contre-intuitifs?
Après les bonnes pratiques et pièges de bases, si tu veux une présentation un peu plus haut niveau, récemment il y a eu ça: https://www.youtube.com/watch?v=UT2K9c66xCU
Ça vient avec l'expérience mais c'est pas mal de remettre les choses à plat avant de se laisser influencer par le design des frameworks qu'on utilise.
Marsh Posté le 21-04-2021 à 21:10:45
DDT a écrit : Par curiosité quels sont les points contre-intuitifs? Après les bonnes pratiques et pièges de bases, si tu veux une présentation un peu plus haut niveau, récemment il y a eu ça: https://www.youtube.com/watch?v=UT2K9c66xCU Ça vient avec l'expérience mais c'est pas mal de remettre les choses à plat avant de se laisser influencer par le design des frameworks qu'on utilise. |
Je suis plus devant le PC mais de tête, j'ai tiqué sur privilégier abstract class VS trait.
On doit d'ailleurs pouvoir lire le contraire sur pas mal de blog Scala, bien que je n'ai rien pour sourcer à l'instant.
Je ne dis pas que l'argumentaire derrière ne tient pas, seulement qu'avant de lire je serais parti dans la direction inverse.
Marsh Posté le 23-04-2021 à 11:05:43
Oui celle-ci est peut-être contre-intuitive, tu as raison. Mais il explique bien dans quel cas l'utilisation systématique de trait peut poser problème, et si ça te concerne pas...
D'ailleurs les traits de Scala 3 supportent des paramètres donc j'imagine qu'on va de moins en moins voir des classes abstraites.
Marsh Posté le 09-05-2021 à 19:27:37
Salut !
Petite question: qu'est-ce que vous avez comme ressource, pour apprendre le scala le plus rapidement possible, pour un javateux ?
Marsh Posté le 09-05-2021 à 20:25:32
C'est pour quelle utilisation ?
Car Scala pour faire du Spark, Akka ou http4s ça va pas vraiment être la même chose.
Le blog de Daniel Westheide est un grand classique : https://danielwestheide.com/books/t [...] -to-scala/
Scala Exercises: https://www.scala-exercises.org/ au moins le chapitre sur la bibliothèque standard.
Marsh Posté le 17-05-2021 à 09:16:35
C'est agité ici
Tu conseilles quel bouquin pour apprendre le FP ? Le Red Book ? On est toujours sur la première édition de 2014, ce n'est pas dérangeant ?
Edit : Functional Programming for Mortals est fait pour moi sur le papier, mais basé sur Scalaz là où j'ai tendance à partir sur du vanilla pour apprendre un nouveau concept. Accessoirement, on est plutôt sur Typelevel dans notre stack actuelle.
Marsh Posté le 17-05-2021 à 09:59:43
Le livre rouge est daté mais je pense que la théorie des catégories n'a pas changé tant que ça.
Si tu veux apprendre comment Scalaz et Cats ont été implémentées ça reste une référence.
Après j'y ai jamais touché et je préfère également les approches plus haut niveau.
Il y avait un version with Cats de FP for mortals mais elle semble avoir disparu.
J'ai déjà mentionné https://www.scalawithcats.com/
Il y a aussi https://eed3si9n.com/herding-cats/ qui est très bon.
Marsh Posté le 17-05-2021 à 13:24:45
Merci.
Selon toi et en venant de l'OOP sans background de math très poussé, l'approche Scala with Cats est suffisante ?
Est-ce que le Red Book conserve un intérêt ?
Marsh Posté le 17-05-2021 à 13:47:23
Scala with Cats me semble largement suffisant.
Les concepts mathématiques sont pas très compliqués cela dit, peu importe la manière dont ils sont approchés.
Marsh Posté le 24-06-2021 à 17:43:11
J'ai deux questions Tapir. Comme d'habitude c'est sans doute trivial mais je sèche un peu et la doc ne m'aide pas.
Code :
|
ça compile mais Intellij me donne du rouge sur formBody avec un hint "No implicits found for evidence Codec[String, Foo, CodecFormatXWwwForUrlencoded]", c'est un peu pénible. Je suis en Scala 2.13 et l'IDE n'a aucune config spécifique.
Ensuite, j'aimerais trouver un moyen élégant pour définir une route qui sera utilisée par un client, en GET, devant porter un header 'authorization: Bearer {access_token}'. Le token est le résultat d'une auth OAuth2 pour info.
Bien évidemment le token est variable.
Code :
|
a l'air dédié a de la lecture ("reads data from the Authorization header" ) et je ne trouve pas de manière élégante de faire autrement. J'ai bien
Code :
|
mais ça m'impose de passer `s"Bearer {accessToken}"` en paramètre plutôt que le token seul et c'est plutôt moche, je pense passer à côté d'un truc obvious.
Merci !
Marsh Posté le 24-06-2021 à 18:15:21
Pour ton premier point j'ai l'impression que c'est IntelliJ qui ne réalise pas que ta classe hérite bien de Product with Serializable.
Si je fais
case class Foo(foo: String) extends Product with Serializable |
Plus de soulignement en rouge.
Marsh Posté le 24-06-2021 à 18:31:54
Pour ta 2e question j'ai pas compris, auth.bearer fonctionne dans un sens (pour le serveur) mais pas dans l'autre (client)?
En regardant le code j'ai bien l'impression que ça fait ce que tu veux...
Marsh Posté le 24-06-2021 à 22:32:54
DDT a écrit : Pour ton premier point j'ai l'impression que c'est IntelliJ qui ne réalise pas que ta classe hérite bien de Product with Serializable. Si je fais
Plus de soulignement en rouge. |
Exact, même comportement de mon côté. C'est quand même pénible, je vais créer un ticket.
DDT a écrit : Pour ta 2e question j'ai pas compris, auth.bearer fonctionne dans un sens (pour le serveur) mais pas dans l'autre (client)? |
Yep my bad, il lui faut les parenthèses.
Tu sais s'il existe un moyen simple de voir la requête client qui serait générée à partir d'un endpoint ?
Comme d'habitude, merci pour tes réponses
Marsh Posté le 11-12-2020 à 18:55:55
MP public entre LeRiton et moi pour l'instant, pour éviter d'entremêler les questions relatives à Scala dans blabla@prog.
Je tenterai de faire quelque chose du premier post s'il y a de la demande pour ça.
Sujets bienvenus:
- écosystèmes fonctionnels: Scalaz, Typelevel, ZIO
- systèmes distribués: Finagle, Akka,
- frameworks web: Play, Akka HTTP, http4s, ...
- bibliothèques et outils en tout genre
- resources pour l'apprentissage
- Scala 3
- Spark
- drama dans la communauté
---------------
click clack clunka thunk