diff --git a/advent-2018/01-simple-json.md b/advent-2018/01-simple-json.md new file mode 100644 index 0000000..9c41ae6 --- /dev/null +++ b/advent-2018/01-simple-json.md @@ -0,0 +1,213 @@ +# German + +Willkommen beim *Advent of Justin* 2018! Ich habe schon darüber Witze gemacht +dass das passieren wird, aber jetzt ist es wohl tatsächlich soweit. + +Für den ersten Tag habe ich mir vorgenommen euch eines meiner schon etwas +älteren Lieblingsprojekte vorzustellen: Simple-JSON. Hierbei handelt es sich um +eine Library die ich ursprünglich im Juli 2017 gebaut habe, mit der Erwartung +dass es nur eine Demo wird. Ich konnte damals noch nicht ahnen dass die Library +ziemlich beliebt werden würde um eine einfache Möglichkeit für die automatische +(De)serialisierung von JSON/Foreign zu haben. Als ich dann damit anfing, +PureScript für die Arbeit zu verwenden habe ich auch gleich damit begonnen +diese Library zu verwenden. + +Was mir an dieser Library so gut gefällt ist nicht unbedingt einmal der +JSON-Teil, aber dass man damit gut demonstrieren kann dass man Typen verwenden +kann um Werte abzuleiten. Das ist auch der Grund warum ich PureScript lieber +benutze als jede andere Sprache die nach JS kompiliert und was ihr in den +meisten meiner PureScript-Posts sehen werdet. + +## "Writing a JSON decoder using Purescript's RowToList" + +In diesem Post erkläre ich genauer was eigentlich ein `Record` ist und wie die +Row Type-Informationen als iterierbare Datenstruktur auf Typebene verwendet +werden können um Decoder und Encoder für JS-Werte abzuleiten: + + + +Früher in diesem Jahr habe ich begonnen etwas Aufwand in die Dokumentation +einiger meiner Libraries zu stecken. Daher gibt es jetzt diese Dokumentation +für Simple-JSON: . +Alles was ein Nutzer dieser Library braucht ist dort zu finden, sogar mit einem +Leitfaden wie man Generics-Rep benutzt um seine eigenen Encodings für +Summen-/Produkttypen zu schreiben. + +Hier ein Auszug aus der Quickstart-Seite: + +```hs +import Simple.JSON as JSON + +type MyRecordAlias = + { apple :: String + , banana :: Array Int + } + +testJSON1 :: String +testJSON1 = """ +{ "apple": "Hello" +, "banana": [ 1, 2, 3 ] +} +""" + +main = do + case JSON.readJSON testJSON1 of + Right (r :: MyRecordAlias) -> do + assertEqual { expected: r.apple, actual: "Hello"} + assertEqual { expected: r.banana, actual: [ 1, 2, 3 ] } + Left e -> do + assertEqual { expected: "failed", actual: show e } +``` + +## P.S. + +The erste Post des *Advent of Justin* ist etwas länger geraten als geplant. +Die nächsten Posts werden kürzer, aber hoffentlich lest ihr dann mehrere davon. + +--- + +# Spanish + +Bienvenidos al [Adviento](https://es.wikipedia.org/wiki/Calendario_de_Adviento) de Justin 2018! Había bromeado sobre hacer esto, pero parece que finalmente va a ser realidad. + +Para el primer día me gustaría presentar a un viejo favorito: Simple-JSON. Esta es una librería que hice en Julio del 2017, con la expectativa de que seria tan solo una demo. Poco podía saber entonces que esta librería se volvería relativamente popular como una forma atractiva de obtener serialización y deserialización automática de JSON/Foreign, y yo mismo me encontré utilizándola cuando comencé a usar PureScript en el trabajo. + +Lo que más me gusta de esta librería ni siquiera es la parte sobre JSON, sino que demuestra que podemos usar tipos para derivar valores a utilizar. Esto es lo que me hace seguir usando PureScript por encima de cualquier otro lenguaje que compile a JS, y lo que verán resaltado en la mayoría de mis posts sobre PureScript. + +## "Escribiendo un decoder JSON usando RowToList en PureScript" + +En este post presento exactamente qué es un Record, y cómo la información sobre la _fila_ de tipos puede ser transformada en una estructura de datos iterable a nivel de tipos para derivar encoders y decoders de valores de JS: + + + +Hacia principios del año, comencé a dedicarle un poco de esfuerzo a documentar algunas de mis librerías. Gracias de ese esfuerzo, ahora tengo la siguiente documentación para Simple-JSON: . Todo lo que un usuario de la librería debería necesitar esta ahí, incluyendo una guía sobre como usar Generics-Rep para escribir tus propios encodings para tipos suma o producto. + +Este es un extracto de la guía de comienzo rápido: + +```hs +import Simple.JSON as JSON + +type MyRecordAlias = + { apple :: String + , banana :: Array Int + } + +testJSON1 :: String +testJSON1 = """ +{ "apple": "Hello" +, "banana": [ 1, 2, 3 ] +} +""" + +main = do + case JSON.readJSON testJSON1 of + Right (r :: MyRecordAlias) -> do + assertEqual { expected: r.apple, actual: "Hello"} + assertEqual { expected: r.banana, actual: [ 1, 2, 3 ] } + Left e -> do + assertEqual { expected: "failed", actual: show e } +``` + +## P.D. + +Bueno, el primer post del Adviento de Justin termino siendo un poco mas largo de lo planeado. Los próximos posts serán mas cortos, así que espero que leas más de estos. + +--- + +# English + +Welcome to the Advent of Justin 2018! I've joked about making this happen, but I guess now it's actually happening. + +For the first day, I figured I would introduce an old favorite: Simple-JSON. This is a library that I first made in July 2017, with some expectation that it would just be a demo. Little did I know, this library actually became fairly popular as a nice way to get automatic de/serialization of JSON/Foreign, and I found myself immediately putting this library to work when beginning to use PureScript for work. + +The thing that I like so much about this library isn't really even the JSON part, but that it shows that you can readily use types to derive values to put to use. This is what makes me keep using PureScript over any other language that compiles to JS, and what you'll see featured in most of my posts about PureScript. + +## "Writing a JSON decoder using Purescript's RowToList" + +In this post, I go through what exactly a `Record` is, and how the row type information can be turned into a type-level iterable data structure to derive decoders and encoders for JS values: + + + +Earlier this year, I started putting some effort into documentation of some of my libraries. From that effort, I have these docs for Simple-JSON now: . Everything a user of this library should need is just about here, with even a guide for how to use Generics-Rep to write your own encodings for sum/product types. + +Here's an excerpt from the quickstart page: + +```hs +import Simple.JSON as JSON + +type MyRecordAlias = + { apple :: String + , banana :: Array Int + } + +testJSON1 :: String +testJSON1 = """ +{ "apple": "Hello" +, "banana": [ 1, 2, 3 ] +} +""" + +main = do + case JSON.readJSON testJSON1 of + Right (r :: MyRecordAlias) -> do + assertEqual { expected: r.apple, actual: "Hello"} + assertEqual { expected: r.banana, actual: [ 1, 2, 3 ] } + Left e -> do + assertEqual { expected: "failed", actual: show e } +``` + +## P.S. + +So the first post of the Advent of Justin ended up being a little longer than planned. The next posts will be shorter, but hopefully you'll read through some more of these. + +--- + +# Japanese + +Justinアドベント2018にようこそ!こんなことやるぞと今まで遊びで言ってきましたが、本当に実現してしまいました。 + +初日である今回は、古いお気に入りである`Simple-JSON`を紹介します。2017年7月に作成を始めたライブラリで、面白い試みと思い発表した、ただのデモでした。意外にもJSON/Foreign(JS)の機械的なデシリアライズ/シリアライゼーションを行う上で、このライブラリはかなりの好評を博しました。私自身もPureScriptを仕事に使うときに、すぐにこのライブラリを使用することができました。 + +このライブラリで本当に大好きな点は、実際にはJSONに関する部分ではなく、型を使用して派生した値を簡単に利用できる点です。これは私がAltJSと呼ばれる他のどの言語よりPureScriptを使う理由であり、PureScriptに関する私の記事の中に頻繁に登場します。 + +## "PurescriptのRowToListを使用したJSONデコーダの記述" + +下記の記事では、正確に `Record`が何であるか確認し、row型の情報を型レベルの派生できるデータ構造に変換してJS値に対するデコーダとエンコーダを導出する方法について説明します。 + + + +今年の上期からいくつかのライブラリのドキュメント作成に力を入れ始めました。結果として、Simple-JSON用のドキュメントをこのようにまとめました。 ユーザーが必要とするこのライブラリに関する情報はすべてここにまとめています。Generics-Repを使ってsum/product型のエンコードを書く方法についてのガイドも含まれています。 + +クイックスタートの抜粋は、次のとおりです。 + +```hs +import Simple.JSON as JSON + +type MyRecordAlias = + { apple :: String + , banana :: Array Int + } + +testJSON1 :: String +testJSON1 = """ +{ "apple": "Hello" +, "banana": [ 1, 2, 3 ] +} +""" + +main = do + case JSON.readJSON testJSON1 of + Right (r :: MyRecordAlias) -> do + assertEqual { expected: r.apple, actual: "Hello"} + assertEqual { expected: r.banana, actual: [ 1, 2, 3 ] } + Left e -> do + assertEqual { expected: "failed", actual: show e } +``` + +## あとがき + +Justinアドベント2018の始まりは想定より少し長くなってしまいました。以降の記事は短くまとめるつもりですので、ぜひご覧になってください。 + +--- + +Thanks to [alech](https://twitter.com/alech) for the German, [mvaldesdeleon](https://twitter.com/mvaldesdeleon) for the Spanish, and @noolbar for the Japanese version! diff --git a/advent-2018/02-psc-package.md b/advent-2018/02-psc-package.md new file mode 100644 index 0000000..5b47e42 --- /dev/null +++ b/advent-2018/02-psc-package.md @@ -0,0 +1,19 @@ +Psc-Package is a tool for PureScript based on package sets -- that a given package should only have one version. This means that for any package that depends on `purescript-effect`, they will all only use `purescript-effect` at the given version, and will be validated to work with it when the package set is verified for consistency. + +And while you could read some literature on why Bower was used as a default for PureScript projects (such as in Harry's blog here: ), it would be more productive to talk about how Psc-Package works and how you can use it today. + +## "Making a new library and using it in your own Psc-Package set" + +In this post, I go through how you can have your own fork of package sets that you can update with your packages. + + + +Since I wrote this post, I've written some dependencies for an alternative package set solution I have called "Spacchetti" (as in, a *packet* of *spaghetti*). In these guides, I have a quick explanation of what Psc-Package is and how it uses package sets: + + + +In a future post, I'll go more into how Spacchetti works by utilizing a programming language called Dhall to generate package set JSON. + +You may also be interested in this post about upgrading a Bower project to Psc-Package: + +You might also want to read about how you can implement your own Psc-Package: diff --git a/advent-2018/03-tanghulu.md b/advent-2018/03-tanghulu.md new file mode 100644 index 0000000..36fc81f --- /dev/null +++ b/advent-2018/03-tanghulu.md @@ -0,0 +1,29 @@ +While most people associate PureScript with row types, capabilites of Symbols has grown with each release of PureScript, to the point that now we have a Symbol Cons type class now in PureScript 0.12.x to split a known symbol to its head and tail. These Symbol type classes let us apply dependently-typed techniques using statically defined type-level strings. + +## "We don't need Peano Numbers in PureScript" + +Of course, any demonstration of dependently-typed techniques would not be complete without some obnoxious sized list demo, so I wrote about one using the type class `Prim.Symbol.Append`: + + + +The gist of this post is that when we look at the definition of the type class and its functional dependencies, we can treat this like addition of two numbers to a sum: + +```hs +class Append (left :: Symbol) (right :: Symbol) (appended :: Symbol) + | left right -> appended + , right appended -> left + , appended left -> right +``` + +That is, + +``` +a <> b == c + | a b -> c + , b c -> a + , a c -> b +``` + +And so with `"." <> ".." == "..."` and the fundeps above, we can get to work making arithmetic work for symbols of period `"."`. Amazing! + +We'll get more into how to use Symbol for more fun things in future posts. diff --git a/advent-2018/04-easy-purescript-nix.md b/advent-2018/04-easy-purescript-nix.md new file mode 100644 index 0000000..5946170 --- /dev/null +++ b/advent-2018/04-easy-purescript-nix.md @@ -0,0 +1,17 @@ +Since the beginning of time, people complained about this combination of obscure technologies of PureScript with Nix. "The derivation on nixpkgs doesn't work!" "This doesn't build!" "There are missing deps!" "Why is the nixpkgs derivation for Psc-Package not updated?" And while many users may consume Nix packages, they often do not understand how to fix broken derivations, let alone know what they even are. Well, it's not entirely their fault for not knowing though. + +So instead of trying to tackle the incredibly hard problem of how to get derivations on Nixpkgs fixed and working "correctly" (whatever that means), I started looking at the problem from the other side: "Why don't I just get to work using Nix to make my own damn derivations for PureScript tooling? It's not like I even like Nixpkgs so much anyway." + +Famous last words. + +## "Using PureScript easily with Nix" + +In this post, I talk about how I set up an easy way to get PureScript tooling installed to use from a project by using nix derivations. This approach ultimately lets us use PureScript tooling by simply running `nix-shell` in the root of a project, and also allows for being able to install tooling to our global environment by feeding an expression into `nix-env`. + + + +Since I wrote this post, some others, especially my friend Pekka in town, helped get the various expressions fixed up so that things work properly also on NixOS. Definitely check this project out if you want to easily and reliably get PureScript (and related) tooling installed and prepared easily: + + + +And so while the Nix language is strange and doesn't provide the best tools, a combination of `nix repl` and tools like `nix-show-derivation` should help you chase down issues you run into. diff --git a/advent-2018/05-type-classes-are-pattern-matching-for-types.md b/advent-2018/05-type-classes-are-pattern-matching-for-types.md new file mode 100644 index 0000000..a7b491b --- /dev/null +++ b/advent-2018/05-type-classes-are-pattern-matching-for-types.md @@ -0,0 +1,64 @@ +# Finnish + +Monet, jotka tutustuvat tarkemmin tyyppiluokkiin, yllättyvät huomatessaan, että "tyyppiluokat" ei itse asiassa tarkoita mitään yhtä asiaa vaan viittaa kokonaiseen kokoelmaan ominaisuuksia ja ideaan tyyppien käyttämisestä samanlaisella tavalla kuin arvoja käytetään. Niinpä kirjoitin tästä aiemmin pahamaineisessa blogipostauksessani "What I've learned since quitting Elm": + +Tänään kutsun teidät oppimaan lisää siitä, mitä tyyppiluokat ja niihin liittyvät ominaisuudet todella ovat: tyyppien hahmonsovitusta. + +## "Type classes and instances are pattern matching for types" + +Tässä kirjoituksessa kerron siitä, kuinka tyyppiluokat ja niiden ilmentymät ovat tietyn lajin tyyppien hahmonsovitusta aivan kuten hahmonsovitus valintalauseella on tietyn tyypin arvojen hahmonsovitusta. Erityisesti käsittelen tätä siitä näkökulmasta, että miten tyyppien hahmonsovitus toimii RowToListin RowList-lajin kanssa: + + + +Koska tämä kirjoitus keskittyy niin vahvasti siihen, miten RowListin hahmonsovitus toimii, laadin myöhemmin muutamia esimerkkejä ja esitelmän, jotka lähtevät liikkeelle yksinkertaisemmalta tasolta. Niissä esittelen suoraan vastaavaa koodia sekä tietotyypeille että itsemääritellyille lajeille ja näiden lajien tietotyypeille. + + + +(lähdekoodi: ) + +Toivottavasti kalvoistani tajuaa jotakin vaikket tietäisikään kuinka RowToList/RowList ja muut sellaiset toimivat. Ja vaikka moni, jonka pitäisi lukea tämä materiaali, ei sitä kuitenkaan tee, toivon että muut voivat tältä pohjalta laatia esityksiä, jotka levittävät tietoa eteenpäin, jotta ihmiset alkaisivat oppia lisää tästä aiheesta. + +--- + +# English + +Many who read more about type classes are surprised to find that "type classes" doesn't actually mean a single thing, but relates to a whole family of features and the idea of working with types at a level similar to working with values. And so, I wrote about this before in my infamous blog post, "What I've learned since quitting Elm +": + +Today, I invite you to learn more about what type classes and its family of features really are: pattern matching for types. + +## "Type classes and instances are pattern matching for types" + +In this post, I talk about how type classes and their instances are pattern matching for types of a kind, just as pattern matching with case expression are pattern matching for values of a Type. I specifically talk about this in terms of how it works with the RowList kind from RowToList: + + + +Since the post is so heavily focused on how RowList matching works, I later prepared some examples and a talk about this topic starting from a simpler level, where I directly show some comparable code of data types and user-defined kinds with data types of that kind: + + + +(source: ) + +Hopefully my slides will make more sense if you're not familiar with how RowToList/RowList and such work. And while many of the people who should read this material really won't, I hope others can make some derivative works that help spread this knowledge to others, such that people start to learn more about this. + +--- + +# German + +Viele, die mehr über Typklassen lernen, sind überrascht, dass "Typklassen" gar nichts einzelnes meint, sondern eher eine Reihe von Eigenschaften und eine Art des Arbeitens mit Typen ähnlich zum Arbeiten mit Werten bezeichnet. Ich habe bereits früher in meinem berühmt-berüchtigten Blogpost „What I've learned since quitting Elm“ (https://qiita.com/kimagure/items/93a42d67a8833f99fe2e#type-classes-is-not-a-single-feature) darüber geschrieben. + +Heute lade ich dich ein, mehr darüber zu lernen, was Typklassen und ihre Familie von Eigenschaften sind. + +## „Typklassen und Instanzen sind Musterabgleich für Typen“ + +In diesem Artikel spreche ich darüber, wie Typklassen und ihre Instanzen eine Art von Mustervergleich für Typen darstellen, genauso wie Musterabgleich mit Case-Ausdrücken Musterabgleich für die Werte eines Typs ist. In Speziellen schreibe ich darüber, wie das für die RowList-Art (Kind) aus RowToList funktioniert: + +https://qiita.com/kimagure/items/08c59fa21adcd6968ae1 + +Da dieser Artikel so stark darauf fokussiert war, wie der RowList-Abgleich funktioniert, habe ich später einige Beispiele und einen Vortrag auf einem niedrigen Niveau vorbereitet, wo ich einigen direkt vergleichbaren Code für Datentypen und nutzerdefinierten Kinds mit Datentypen dieser Kinds zeige: + +https://speakerdeck.com/justinwoo/type-classes-pattern-matching-for-types + +(Quelle: https://github.com/justinwoo/purescript-typelevel-intro/blob/master/slides.md) + +Hoffentlich ergeben diese Folien mehr Sinn, auch wenn du nicht mit RowToList/RowList und ähnlichem vertraut bist. Auch wenn viele, die das hier lesen das nicht tuen werden, hoffe ich, dass einige abgeleitete Werke erstellen werden, die dieses Wissen weiterverbreiten, sodass mehr Leute davon erfahren. diff --git a/advent-2018/06-home-run-ball.md b/advent-2018/06-home-run-ball.md new file mode 100644 index 0000000..491f655 --- /dev/null +++ b/advent-2018/06-home-run-ball.md @@ -0,0 +1,51 @@ +# English + +We've long seen row types being used to encode information in PureScript, with the built-in Record types (`data Record :: # Type -> Type`), with Polymorphic Variants (via the purescript-variant library), or even the old Eff type (which was not very useful: ). But not many people take enough advantage of them for how they hold information you can use. While many users of variant will take advantage of the `match` function and others that work by iterating the row type information via RowToList, they do not make their own implementations that use these. + +## "Fun Row-typed Validation with Purescript-Home-Run-Ball" + +One of the most fun things I worked on last year used row type information to store what validation should and have been performed on a value. And so, when this refinement type information is readily available, we are able to automatically derive functions to get a [Validation](https://qiita.com/kimagure/items/f75befebdd37f6e8879f) out of these values, with a newtype carrying the validations that have been performed. + + + +By using RowToList, we can iterate over the validations to be performed, and by producing a newtype with the validations row type parameter, we can use regular extensible row types for requiring validation sets, e.g. + +```hs +onlyOnApples + :: ValidatedValue (beginsApple :: BeginsWith "Apple") String + -> String +onlyOnApples _ = "U R COOL" + +onApples + :: forall r + . ValidatedValue (beginsApple :: BeginsWith "Apple" | r) String + -> String +onApples _ = "U R COOL" + +``` + +# German +Row-Typen werden schon lange verwendet, im Informationen in PureScript darzustellen, mit den eingebauten Record-Typen (`data Record :: # Type -> Type`), mit polymorphen Variants (über die purescript-variant Bibliothek) oder mit dem alten Eff-Typen (der nicht sehr nützlich war ). Aber viele Menschen nutzen das, aufgrund der Wege, wie man die enthaltene Information verarbeiten kann. Viele Variant-Benutzer verwenden die `match` Funktion und andere, die funktionieren, indem sie über die Row-Typ Informationen via RowToList iterieren. Kaum jemand aber erstellt seine eigenen Funktionen, die so arbeiten. + +## "Spaßige Row-getypte Validierung mit PureScript-Home-Run-Ball" + +Eine der spaßigsten Sachen, an denen ich im letzten Jahr gearbeitet habe, verwendete Row-Tppe Informationen um zu speichen, welche Validierung durchzuführen ist und welche schon durchgeführt wurde. Und so konnten wir mit diesen zusätzlichen Typinformationen automatisch eine [Validation](https://qiita.com/kimagure/items/f75befebdd37f6e8879f) ableiten, mit einem newtype, der die ausgeführten Valdierungen getragen hat. + + + +```hs +nurAufAepfel + :: ValidatedValue (beginntMitApfel :: BeginsWith "Apfel") String + -> String +nurAufAepfel _ = "Du bist cool." + +aufApfel + :: forall r + . ValidatedValue (beginntMitApfel :: BeginsWith "Apfel" | r) String + -> String +onApples _ = "Du bist cool." +``` + +--- + +Thanks to [BenBals](http://twitter.com/benbals) for the German translation! diff --git a/advent-2018/07-spacchetti.md b/advent-2018/07-spacchetti.md new file mode 100644 index 0000000..8fc03c2 --- /dev/null +++ b/advent-2018/07-spacchetti.md @@ -0,0 +1,24 @@ +For a long while now, people have complained that managing a package set file in a giant JSON file is not very nice. While I think editing JSON is at least nice in that it's universal, I agree that overall it isn't that nice. + +The other major complaint that people have had is that they don't want to fork the package sets. I think this is kind of silly, but I will at least say the next step of this is rather annoying -- maintaining separate tags if you want to have specific packages that are different between projects, if you want to not have your projects use a common master package set. While you really should consolidate your package sets if you want to have interoperability of modules and encodings between your projects, I admit that there must be *some* kind of better solution out there. + +## "Managing Psc-Package sets with Dhall" + +In this post, I introduced my Spacchetti project: + + + +This project lets you maintain package sets not as a blob of JSON, but rather as a Dhall source file, using the Dhall programming language. This then lets us use the full power of the Dhall language to build our package set, such as being able to merge a bunch of records to get our total package set, where we can merge new values into this record for new packages and override existing entries. + +This also means that local overrides for packages are nothing but using a Dhall source file to import the parent package set from a remote url using Dhall's remote import feature: + +```hs + let mkPackage = + https://raw.githubusercontent.com/spacchetti/spacchetti/140918/src/mkPackage.dhall + +in -- ... +``` + +There's a CLI that helps you do this without too much trouble: + +Since I wrote this post, I've also written a large amount of documentation explaining how everything here works, from how Psc-Package itself works, why Dhall is used here, how to use this package set, and how to set up a project with local overrides for packages. See the guide here: diff --git a/advent-2018/08-type-level-path-parameters.md b/advent-2018/08-type-level-path-parameters.md new file mode 100644 index 0000000..1a0584a --- /dev/null +++ b/advent-2018/08-type-level-path-parameters.md @@ -0,0 +1,17 @@ +Typically, approaches to represent parameterized paths in the type level have required making some kind of type-level operator of kind `Type -> Type -> Type`, so that you could define routes in a manner such as `type MyRoute = Literal "hello" / Param "name"`. While this seems "explicit", it also seems at least a little bit silly: why can't we use Symbols to define these routes? + +Csongor wrote about how Symbol.Cons was implemented for PureScript, with an example library for deriving variadic functions here: . He also then made a record-based formatting library here: . Surely we can take advantage of these to make a type-level path solution? + +## "Well-typed path params in PureScript 0.12" + +In this post, I talked about how we can use the type-level parsing result from record-format to make a path: + + + +Knowing that we are going to parse URLs, we can use `/` as delimiters, where we can match literal and variable sections. In building up the variable results, we can use [Record.Builder](https://pursuit.purescript.org/packages/purescript-record/1.0.0/docs/Record.Builder) to build up a record of the parameters we have parsed into a record of strings, where the key is the name that we gave our parameter in this scheme: + +```hs +url :: SProxy "/hello/{name}/{age}" +``` + +In the future, we'll talk about how we can add optional type annotations to this Symbol, so that we don't need to work with a record of strings being converted by key to a proper heterogeneous record. diff --git a/advent-2018/09-psc-package2nix.md b/advent-2018/09-psc-package2nix.md new file mode 100644 index 0000000..33be826 --- /dev/null +++ b/advent-2018/09-psc-package2nix.md @@ -0,0 +1,15 @@ +So while Psc-Package is a nice tool to figure out our dependencies from a package set and a list of direct dependencies, it leaves a lot to be desired when we think about how packages are downloaded. Do we need it to have git fetch our dependencies whenever we don't have them already installed? + +And while many will say that Psc-Package indeed shouldn't use git to fetch every dependency, I think many will point out that even if you tried to store these, you'd need to verify the contents somehow, never mind the endless hell that is trying to figure out where you can actually write files on a user's system globally without trouble (well, ignoring all of the unprincipled approaches of shitting files into a user's HOME directory somewhere). + +So what if instead of trying to make yet another shitty solution, we can use a solution that does try to do the right thing? If we use Nix for this, we can write derivations for how the dependencies are to be fetched, and those will be hashed. When these derivations are run, Nix will create store entries for these derivations. How do we generate this from a psc-packages.json file though? + +## "Nix-ify your Psc-Package dependencies" + +In this post, I talked about how the psc-package2nix tool works, by using the package set and the direct dependencies to generate derivations for each package with the correct sha256 hash needed to verify the contents of the dependency sources. + + + +This also goes into how dependencies are solved for in Psc-Package by traversing the transitive dependencies. + +With the Psc-Package2Nix tool, we can now leverage Nix to download and cache dependencies for our projects, where only in the final step, we can use a small derivation to copy the dependencies from the store into the appropriate directories in `.psc-package/{set}/{package}/{version}`. This means that between projects, changes in package sets, and re-installs, we don't have to download any dependencies redundantly, and can instead have Nix handle the details of when new dependencies need to be downloaded. diff --git a/advent-2018/10-datatype-generic-programming-for-generating-typescript.md b/advent-2018/10-datatype-generic-programming-for-generating-typescript.md new file mode 100644 index 0000000..f092bdf --- /dev/null +++ b/advent-2018/10-datatype-generic-programming-for-generating-typescript.md @@ -0,0 +1,42 @@ +Many people really like data types in PureScript, for very good reasons. Data types are actually really amazingly good, especially sum types. The best part about data types is that we have various ways to pattern match on them, matching on where we have constructors of data types and their arguments. + +What happens when we want to actually get information about our data types? If we think about how our data types are defined, we can extract some more general information about our types: + +```hs +data Fruit = Apple | Banana String + +-- We have a data type Fruit +-- for which we have a Sum of two arguments: +-- * Constructor "Apple", of no arguments +-- * Constructor "Banana", of one argument: +-- * String +``` + +We could go and define a generic set of data types that describe this shape, but we don't need to, as this is a well-known concept known as Datatype Generics, where the compiler can derive the generic form for us to use: + +```hs +import Data.Generic.Rep as GR +import Data.Generic.Rep.Show (genericShow) + +data Fruit = Apple | Banana String + +-- note the underscore at the end for the `rep` parameter of class Generic +derive instance genericFruit :: GR.Generic Fruit _ + +instance showFruit :: Show Fruit where + show = genericShow +``` + +So in this example, we were able to get an implementation of `show` for free, by using the datatype generics implementation of `show` with a compiler-derived instance of `Generic` for `Fruit`. + +## Datatype-Generic programming for generating TypeScript code from Purescript + +In this post, I go over how we can use datatype generics for codegen of TypeScript types from some PureScript type definitions. + + + +While the newest version of the library talked about in the article now uses RowToList as TypeScript code generation largely targets Records and Variants, the ideas in it are more broadly applicable to any data types. Indeed, in PureScript 0.12, we no longer generate the generic representation for records (and that's a good thing, leave unto RowToList what is RowToList's), but we still use datatype generics for data types. + +A more readily applicable tutorial on how to use generics-rep can be found here: + + diff --git a/advent-2018/11-inferred-record-types-with-simple-json.md b/advent-2018/11-inferred-record-types-with-simple-json.md new file mode 100644 index 0000000..6e8367d --- /dev/null +++ b/advent-2018/11-inferred-record-types-with-simple-json.md @@ -0,0 +1,77 @@ +# English + +With most libraries that work purely with instances, working with an exception is quite annoying. Take for an example the case where you have a record of a couple different fields. If all of the fields have an instance for your decoding type class, everything works just fine. What if just one of those fields is actually a date, but in JSON is represented as a state? Then your entire record no longer has an instance for decoding. + +But surely, we should be able to hot swap this, right? There's no catch-all instance for printing and parsing date strings, since everyone likes to use different formats. Sure, we might newtype the field itself, but then we pay the penalty of having to carry around a newtype for whatever date type we use elsewhere. We could also newtype the type that carries this type, but then we need to manually write out how the other fields of the record are read too. Surely there must be some kind of solution where we can tell the compiler to do the default decoding of all of the other fields save one, which we'll handle ourselves. + +Well, considering that PureScript has anonymous record types, couldn't we get the compiler to infer some other type here that we can work with? Yeah, we can! + +## "Modified JSON parsing for free with PureScript-Simple-JSON" + +In this post, I talked about how we can get the compiler to infer for us a type that is decoded from some JSON that we can then further manually decode and replace properties. + + + +Since I wrote this post, I also wrote a page in the Simple-JSON guide: + + + +In short, inferred record types let us to do this: + +```hs +type RecordMisnamedField = + { cherry :: Int + } + +readRecordMisnamedField :: String -> Either Foreign.MultipleErrors RecordMisnamedField +readRecordMisnamedField s = do + inter <- JSON.readJSON s + pure $ Record.rename grapeP cherryP inter + where + grapeP = SProxy :: SProxy "grape" + cherryP = SProxy :: SProxy "cherry" +``` + +And this will read JSON with `grape :: Int` from the inferred context. + +--- + +# Japanese + +純粋な環境で動作するライブラリでは、例外の処理を行うことは非常に面倒です。異なるフィールドを持つレコードを扱う場合の例を考えてみましょう。もしもすべてのフィールドに対して型クラスからのデコードが実装されているのであれば、問題なく動作するでしょう。これらのフィールドのうちの1つだけが実際には日付ですが、JSONでは状態(null 許容)として表される場合どうでしょうか。これは、レコード全体がデコードの実装を持たなくなることを指しています。 + +こういったとき好きに変換することはできないのでしょうか。誰もが異なったフォーマットを使うのですから、日付文字列の出力と解析を行う万能の実装はないでしょう。フィールドを新しい型に組み込む場合、他の場所で使っているdata型については、新しい型を持ち歩かなければならないというペナルティを受け入れる必要があります。この型を持つ型を新しい型にすることもできますが、レコードの他のフィールドにも、どのように読み出しを行うかを手動で書き出す必要があります。他のすべてのフィールドを既定のデコードで動作するようコンパイラに指示することができる解決策がなければなりません。 + +PureScriptに匿名レコード型があることを考慮すると、他の型をコンパイラに推論させることはどうでしょうか。そう、これは可能なのです。 + +## "PureScript-Simple-JSONを使用して自由にJSONを修正する" + +以下の記事では、JSONからデコードされた値を手動でデコードしてプロパティを変更する型をコンパイラに文脈から推論させるために、どのように記述するかを話しました。 + + + +この記事にも書いたように、Simple-JSON用のドキュメントをこのようにまとめました。 + + + +簡単に言えば、推測されたレコード型は、以下のように使用できます。 + +```hs +type RecordMisnamedField = + { cherry :: Int + } + +readRecordMisnamedField :: String -> Either Foreign.MultipleErrors RecordMisnamedField +readRecordMisnamedField s = do + inter <- JSON.readJSON s + pure $ Record.rename grapeP cherryP inter + where + grapeP = SProxy :: SProxy "grape" + cherryP = SProxy :: SProxy "cherry" +``` + +これは推論された前後関係から `grape :: Int`を使ってJSONを読み込みます。 + +--- + +Thanks to @noolbar for the Japanese translation! diff --git a/advent-2018/12-tortellini.md b/advent-2018/12-tortellini.md new file mode 100644 index 0000000..e400654 --- /dev/null +++ b/advent-2018/12-tortellini.md @@ -0,0 +1,13 @@ +For whatever strange reason, many INI libraries only work by representing your INI files as hashmaps, where the most common representation is `Map String (Map String String)`. While people often live with this, I wanted to get greater type safety when working with INI configuration files. + +I ended up writing two libraries: one in PureScript and one in Haskell. + +## "The Tale of Two Tortellini: making record based libraries in PureScript and Haskell" + +In this post, I wrote about the process involved in writing each library and how they compare when working with a model of fields to sections, sections to document. + + + +While the PureScript version was able to take advantage of anonymous records and the row types inside, the Haskell version was limited in that every section of the INI document needed to be its own record type. While it isn't that much of a shortcoming, it does show that having proper anonymous records would go a long way here. + +And while this post is about INI files, the techniques covered here should help anyone write their own decoding code for JSON or any other format where they can work with a parsed structure. diff --git a/advent-2018/13-type-level-annotated-string-templates.md b/advent-2018/13-type-level-annotated-string-templates.md new file mode 100644 index 0000000..10bb145 --- /dev/null +++ b/advent-2018/13-type-level-annotated-string-templates.md @@ -0,0 +1,28 @@ +Previously, I covered my post about well-typed paths using Symbols with formatting parameters, such that we can derive a parsing function from a Symbol proxy like so: + +```hs +-- inferred type: +parseHelloURL :: String -> Either String { name :: String, age :: String } +parseHelloURL = parseURL (SProxy :: SProxy "/hello/{name}/{age}") +``` + +But really, we might like to read `age` here as an `Int`, and we could provide this information as a type annotation. And we can! + +## "Parsing type-level strings to extract types" + +In this post, I go through how we can modify the previous implementation to conditionally parse out a type annotation from our Symbol, by using the same techniques for parsing piecewise with instance chain groups as done in Record-Format and other libraries. + + + +This way, we can instead work with URLs that have their types annotated like so: + +```hs +parseHelloURL :: String -> Either String { name :: String, age :: Int } +parseHelloURL = parseURL (SProxy :: SProxy "/hello/{name:String}/{age:Int}") +``` + +## "Simple Routing based on parsing type-level strings" + +There's also a follow-up post to this that applies classic RowToList techniques to allow for a row of these routes to be tried to product an Either of no matches and a Variant of the routes: + + diff --git a/advent-2018/14-implementing-your-own-compiler-type-class.md b/advent-2018/14-implementing-your-own-compiler-type-class.md new file mode 100644 index 0000000..bf17971 --- /dev/null +++ b/advent-2018/14-implementing-your-own-compiler-type-class.md @@ -0,0 +1,19 @@ +While the current set of compiler type classes are pretty nice, sometimes there's not a class for something I'd like to do. Other times, there's an operation I could perform manually with the existing classes, but the inference and performance of solving these constraints is terrible. + +Whatever the reason, it would be nice to define some of my own compiler type classes. + +## "Implement your own compiler type class in PureScript" + +In this post, I go over what all is involved in lining up the pieces in the PureScript compiler codebase to add your own compiler-solved type class. + + + +Once we have all of the pieces in place from a few dozen lines of adding new definitions, documentation, and functional dependencies information, we can write the core logic that solves our instances. For example, checking if a Symbol pattern is contained within another Symbol and getting a Boolean-kinded result: + +```hs +containsSymbol :: Type -> Type -> Type -> Maybe (Type, Type, Type) +containsSymbol patt@(TypeLevelString patt') sym@(TypeLevelString sym') _ = do + flag <- T.isInfixOf <$> decodeString patt' <*> decodeString sym' + pure (patt, sym, TypeConstructor $ if flag then C.booleanTrue else C.booleanFalse) +containsSymbol _ _ _ = Nothing +``` diff --git a/advent-2018/15-bower-to-nix.md b/advent-2018/15-bower-to-nix.md new file mode 100644 index 0000000..fdea58e --- /dev/null +++ b/advent-2018/15-bower-to-nix.md @@ -0,0 +1,13 @@ +Even though nobody needs to suffer installing their project dependencies with Bower anymore, we still have some people with projects started in Bower from the past couple of years who might not have changed. Worse, sometimes we have users who have a project started with Bower, not knowing that they have much better options to work with. + +A long time ago, I wrote about how to upgrade from Bower to Psc-Package: + +And while this is enough to work with our projects in a pretty modern way, Psc-Package itself is a very simple tool that doesn't try to introduce much. If we want a robust way to verify the contents of dependencies we pull down and store them to be shared between multiple installations, we're going to have to come up with good ways to do this. + +## "Upgrade from Bower to Nix with Psc-Package2Nix" + +In this article, I wrote about how to upgrade a project using Bower to Nix via Psc-Package2Nix: + + + +With this, we can use a simple `psc-package.json` file to specify the package set definitions and direct dependencies just like with regular Psc-Package usage, but then we can use Psc-Package2Nix to get a set of derivations we can use to install our dependencies. diff --git a/advent-2018/16-dynamically-typed-errors.md b/advent-2018/16-dynamically-typed-errors.md new file mode 100644 index 0000000..d53a0a8 --- /dev/null +++ b/advent-2018/16-dynamically-typed-errors.md @@ -0,0 +1,15 @@ +Many times when we deal with errors from impure routines in PureScript applications, we have to think about errors at multiple stages. Is it an exception from the runtime that is caught as an Error? Or is it an error that we expect to handle in some way? + +And while many will end up using `ExceptT` to model the expected errors, I don't find this particularly pleasing to use with impure code, where I still end up with two different layers of errors to handle. + +What if I could just put information in an Error? I would really like to use the normal errors too, but the problem is that I can only store strings inside of the normal JS Error, so I have to find another way to transport error information. But is it really far off from using Error? + +## "Pseudo-dynamically typed errors in PureScript" + +In this post, I talk about putting my error information in a Variant value, and putting it into a subclass of Error: + + + +By using a subclass of error, I can stuff it into wherever Error goes, and then I can retrieve this error information by using some reading operations, where I can make sure that 1) I have an instance of my VariantError class and 2) the key "type" in the runtime representation of the Variant contained is within the set of keys expected. + +And by using this, I no longer need to handle errors on multiple levels and can get away with not having to deal with `ExceptT MyError Aff`. While many might disagree with this approach, this really does make working with my code much easier, especially for people who are not familiar with transformers. diff --git a/advent-2018/17-union-constraints.md b/advent-2018/17-union-constraints.md new file mode 100644 index 0000000..4e478fe --- /dev/null +++ b/advent-2018/17-union-constraints.md @@ -0,0 +1,22 @@ +Many times when we work with JS libraries from PureScript, we find that many methods take in a record for their arguments that may have any fields that are a subset of a total set of valid properties. But how should we go about modeling this? + +Luckily in PureScript 0.11.x, we gained the ability to talk about Unions of rows, where we can declare that the combination of rows `left` and `right` can form a total set `union`: + +```hs +class Union (left :: # Type) (right :: # Type) (union :: # Type) + | left right -> union + , right union -> left + , union left -> right +``` + +We can see from the fundeps that if we have the `left` and `union`, we can determine `right` here. How would we exploit this to work with JS APIs? + +## "Unions for Partial Properties in PureScript" + +In this post, I talked about how to take advantage of Union to partially type echarts: + + + +The approach used here matches that of React-Basic, a library from the folks at Lumi, where we can use the concrete input provided by the user and the total set of properties we know exist for a given method together to determine the unspecified complement. While we don't care about the complement very much, we at least can tell from this that we have specified a valid subset of inputs in this way. + +This gives us the ability to talk about most JS interfaces that use partial sets of inputs, so this is definitely worth looking into the next time you run into object inputs. diff --git a/advent-2018/18-jajanmen.md b/advent-2018/18-jajanmen.md new file mode 100644 index 0000000..d033a6b --- /dev/null +++ b/advent-2018/18-jajanmen.md @@ -0,0 +1,31 @@ +In previous posts, I wrote about using type-level parsing for URLs. While that's nice and all, it's not necessarily what seems readily usable to solve some problem. Luckily, I have a worse problem to talk about, but with a nice solution. + +Often, I use SQLite from PureScript by using the sqlite3 library. This library lets me supply parameters by naming them, and providing a record that has properties where the labels match the ones in the query, so that the parameters are applied to the query. This is nice, but in typical usage, it's untyped in that I can easily forget to specify properties and cause my query to always fail. + +Well, I can solve that, can't I? I have a static query that I'm going to be using the vast majority of the time, so I can put those Symbol parsing skills to use. + +## "Well-typed parameterized SQLite parameters with PureScript" + +In this post, I wrote about how we can parse a type-level string to extract parameters that make up a record of parameters we need for our query, where the Symbol can be simply reflected to provide to sqlite3: + + + +Tl;dr: + +```hs +-- the type signature here is inferred: +getEm + :: forall a b. J.AllowedParamType a => J.AllowedParamType b + => SL.DBConnection -> { "$name" :: a, "$count" :: b } -> Aff Foreign +getEm db = J.queryDB db queryP + where + queryP = SProxy :: SProxy "select name, count from mytable where name = $name and count = $count" +``` + +Since I wrote this post, I've given a couple of talks about this library and how people might get to using it. + +Long form presented in Milan: + +Short form presented in HaskellX as a lightning talk: + +There's even a video of the HaskellX talk: diff --git a/advent-2018/19-ffi-in-purescript.md b/advent-2018/19-ffi-in-purescript.md new file mode 100644 index 0000000..9f66636 --- /dev/null +++ b/advent-2018/19-ffi-in-purescript.md @@ -0,0 +1,55 @@ +# Spanish + +Una de las cosas más convenientes al utilizar PureScript es su Interfaz de Funciones Foráneas (FFI), ya que resulta bastante permisiva sobre la forma de trabajar con ella. Sin embargo, muchas veces las personas no aprenden suficiente acerca del funcionamiento de la FFI, usualmente porque piensan que va a resultar muy complicado o demandar demasiado esfuerzo, o porque lo consideran "inferior" a lo que ellos hacen. Bueno, a este ultimo grupo no se como podemos ayudarlo, pero al primero definitivamente podemos dotarlo de poder con algunas referencias y herramientas. + +## "Dándole poder al usuario de FFI en PureScript" + +En este post hablé sobre las distintas técnicas comunes de FFI en PureScript, tales como funciones con efectos sin currificar, tipos de datos opacos, conversión de Promesas a Aff, y más: + + + +Es importante notar que si querés validar alguno de los valores retornados desde JS, podés directamente usar Simple-JSON sobre los valores de tipo `Foreign`. + +Si leer un post entero resulta demasiado, pienso que un muy buen uso de tu tiempo es al menos leer la documentación de Effect.Uncurried: + + + +--- + +# English + +One of the most convenient things about using PureScript is the FFI, which is quite lenient in letting you choose how you want to work with it. However, many times people don't learn enough about how the FFI works, usually because they think that it will be too hard/too much of a hassle, or see it as "below" them. Well, I don't know how we can help people in the latter camp, but the former camp can definitely be empowered with some references and examples. + +## "User empowerment of FFI in PureScript" + +In this post, I talked about the various common FFI techniques in PureScript, such as uncurried effect functions, opaque data types, Promises to Aff, and more: + + + +It's important to note that if you do want to validate some returned value from JS, you can readily put Simple-JSON to work on `Foreign`-typed values. + +If reading a whole post seems like too much, I think your time is most well spent at least reading the Effect.Uncurried docs: + + + +--- + +# 日本語 + +FFIはPureScriptにおいて最も強力な機能の一つであり、様々なアプローチで活用することができます。しかしながら、多くの人はFFIがどう機能しているか知りません。なぜなら、一般的にFFIは「学習コストが高そう」と思われていたり、なぜだか過小評価されていたりするためです。後者のケースについてはどうにもできませんが、前者については間違いなく、この記事で紹介するリンクと実例が参考になるでしょう。 + +## "User empowerment of FFI in PureScript" + +次の記事では、PureScriptにおけるFFIの一般的なテクニックについて書きました。 **uncurried effect functions** , **opaque data types** , **Promises to Aff** などです。 + +https://qiita.com/kimagure/items/0ce4d9d2792dd110ee45 + +また、JSから返された値をバリデートしたい場合、[Simple-JSON](https://github.com/justinwoo/purescript-simple-json)を使って、その値を`Foreign`-typed な値として扱うことができます。 + +もし紹介した記事を最後まで読むのが大変なら、最低限 `Effect.Uncurried` の公式ドキュメントを読めば良いでしょう。 + +https://pursuit.purescript.org/packages/purescript-effect/2.0.0/docs/Effect.Uncurried + +--- + +Thanks to [mvaldesdeleon](https://twitter.com/mvaldesdeleon) for the Spanish and @e_ntyo for the Japanese translations! diff --git a/advent-2018/20-pandoc-with-nix.md b/advent-2018/20-pandoc-with-nix.md new file mode 100644 index 0000000..9352723 --- /dev/null +++ b/advent-2018/20-pandoc-with-nix.md @@ -0,0 +1,11 @@ +This time, we'll take a total break from PureScript to talk about something everyone hates: setting up a way to make slides with latex-beamer. + +Luckily, there are two big things I'll do to make this process way less painful: 1) I'll use Markdown and 2) I'll use nix to install the dependencies in a shell, so this is actually reproducible. + +## "Easy Markdown to Beamer with Pandoc and Nix" + +In this post, I go through how I used the combine function provided for the texlive derivation to stuff in all of the texlive packages I would need: + + + +With this setup, I no longer spend so much time with Google Slides on making my slides, and instead have more time to focus on and edit the content. Not that it was so bad to use Slides before when I could copy-paste from VSCode to get formatted code, but editing content and getting code laid out in Slides was so arduous before, where an average talk would take me over 10 hours to put together, whereas now I can easily get it done in 1 hour if I know what I want to talk about. diff --git a/advent-2018/21-instance-chains-and-kazunoko.md b/advent-2018/21-instance-chains-and-kazunoko.md new file mode 100644 index 0000000..258efa4 --- /dev/null +++ b/advent-2018/21-instance-chains-and-kazunoko.md @@ -0,0 +1,34 @@ +Previously, I wrote about usage of instance chains when parsing Symbols, but it can be quite difficult to see just in that context. Even though the idea of how instance chains works in PureScript is that they are groups of instances that may have overlapping instance heads, it would help to see an easier example at work. + +## "Fun type-level literal number arithmetic with instance chains" + +In this post, I wrote about an amusing way to work with type-level numeric literals in PureScript: + + + +Here, I defined successions of natural numbers, with an instance chain group so that I could catch overflows of my tiny range of zero to ten. + +```hs +class Succ (i :: Symbol) (o :: Symbol) | i -> o, o -> i +instance zeroSucc :: Succ "zero" "one" +else instance oneSucc :: Succ "one" "two" +else instance twoSucc :: Succ "two" "three" +-- ... +else instance noSucc :: + ( Fail (Text "i don't know how to count any bigger than ten or less than zero") + ) => Succ i o +``` + +Then I could define addition and subtraction in a fairly simple way by working by the left operand. For the case of addition, this meant that I wanted to add by simply iterating on the case that given `left`, `right`, and `output`, `left` could be decremented by taking the reverse of succession: + +```hs +class Add (l :: Symbol) (r :: Symbol) (o :: Symbol) | l -> r o +instance zeroAdd :: Add "zero" r r +else instance succAdd :: + ( Succ l' l + , Succ r r' + , Add l' r' o + ) => Add l r o +``` + +And while this is a really silly example, I hope this gives you some ideas on what you might like to try out if you have a type class with some tricky instances. diff --git a/advent-2018/22-naporitan.md b/advent-2018/22-naporitan.md new file mode 100644 index 0000000..a2b3e8c --- /dev/null +++ b/advent-2018/22-naporitan.md @@ -0,0 +1,21 @@ +To group a bunch of proxies together either for convenience or for RowToList-directed type-level verification, you might choose to put them in a record. However, this usually leaves us in a position where we then need to go through and write out the value of our proxy literally, leading to a load of repetition and pain, where we need to make sure we have actually declared all of the fields in our record of proxy types below. + +If PureScript has taught us anything, it's that if we have static knowledge about something, we should be able to derive its value. + +## "Reflecting a record of proxies and keys of row types" + +In this post, I wrote about how we can once again use RowToList with the combination of Record.Builder to reflect a record of proxies into its value: + + + +Just like that, we don't need to write any error-prone boilerplate, since we can use the compiler to derive the value: + +```hs +proxies :: + { apple :: Proxy Int + , banana :: Proxy String + } +proxies = N.reflectRecordProxy +``` + +I hope even if this example seems a bit silly, this really gives you some ideas on how powerful languages really can derive values for you from the types, and how this is really incredibly useful when you make a conscious choice of what you'd like to get out of your system. Otherwise, there would be a much smaller difference between PureScript and other languages. diff --git a/advent-2018/23-inferred-error-types.md b/advent-2018/23-inferred-error-types.md new file mode 100644 index 0000000..0ace362 --- /dev/null +++ b/advent-2018/23-inferred-error-types.md @@ -0,0 +1,35 @@ +When we write sophisticated type-level code, usually the types should work such that they are fully determined in some way. This means that as long as we give the intermediate a value to correspond to, we'll have the inferred type available to us that we can use the PureScript IDE tooling to insert. + +But how do we write a basic assertion that this inferred type should match something, instead of trying to manually verify that types infer to the result we expect? + +## "Expecting Inferred Types (feat. Custom Type Errors)" + +In this post, I talked about how we can use a simple two-parameter type class to assert that an inferred type should equal an expected type: + + + +The main that this works with by having two instance, with the main instance being that the first and second parameters should be equal. Then an overlapping instance is defined using instance chain groups, which uses the Prim.TypeError module to make a custom Fail constraint, such that we can get an error like so: + +```hs +test2 :: Unit +test2 = + let + -- this will error correctly: + expectedP = Proxy :: Proxy String + -- A custom type error occurred while solving type class constraints: + -- + -- The expected (first) and actual (second) types did not match: + -- String + -- Unit + -- + -- while applying a function expectInferred + -- of type ExpectInferred t0 t1 => Proxy t0 -> t1 -> Unit + -- to argument expectedP + -- while inferring the type of expectInferred expectedP + -- in value declaration test2 + simpleValue = simpleMethod (Proxy :: Proxy String) + in + expectInferred expectedP simpleValue +``` + +While many people complain about error messages in PureScript, we can see that the error message here is fairly straightforward if we slow down and read through its parts, telling us that the custom type error occured from the application of the `expectInferred` function. Really, PureScript errors are surprisingly good given the amount of type information we routinely work with, but you have to give it some patience. diff --git a/advent-2018/24-modeling-chart.js-spec-building-with-rows.md b/advent-2018/24-modeling-chart.js-spec-building-with-rows.md new file mode 100644 index 0000000..ddd5e1b --- /dev/null +++ b/advent-2018/24-modeling-chart.js-spec-building-with-rows.md @@ -0,0 +1,57 @@ +# English + +While we've previously talked about ways to model JS charting library specs by using Row.Union, another interesting viewpoint to look from instead is how to allow for building an arbitrary spec just based on restricting what properties can be set based on what kind of chart we are building. + +## "Using Rows and RowToList to model Chart.js spec building" + +In this article, I talked about how we might model spec-building for Chart.js, by using a row type to specify which functions for builder components are valid for which types of charts: + + + +The problem that this article mainly focused on was that we needed to get the intersection of two row types when composing chart builders: + +```hs +newtype ChartBuilder + (appliesTo :: # Type) + (input :: # Type) + (output :: # Type) + = ChartBuilder (Builder (Record input) (Record output)) +``` + +So to compose two of these chart builders together, we need to make sure that the new chart builder that we output has a `appliesTo` parameter that is valid. So given the composition of `( a :: _ , b :: _ )` and `( a :: _ )`, we need to produce the output `( a :: _ )`. + +While it would be convenient if we could have multiple constraints solved simultaneously to allow us to express a system of Union constraints, we do need to actually implement this in terms of RowToList as only one constraint can be fully determined at a time. I wrote about this problem more in the follow-up article, "Making Diffs of differently-typed Records in PureScript", so I hope you'll read through it sometime: + + + +--- + +# Japanese + +以前に、Row.Unionを使用してJSのグラフライブラリ仕様をモデル化する方法について説明しましたが、目的とするグラフでどのような種類のプロパティを設定できるかを制限し、これに基づいて任意の仕様を構築する方法があります。 + +## "row型とRowToListを使ってChart.js仕様をモデル化する" + +以下の記事では、row型を使用してビルダーコンポーネントの関数がどの型のグラフに対して有効かを指定することによって、Chart.jsの仕様作成をモデル化する方法について説明しました。 + + + +この記事で注目した問題は、`ChartBuilder`を作成するときに2つのrow型の合成を取得する必要がある点です。 + +```hs +newtype ChartBuilder + (appliesTo :: # Type) + (input :: # Type) + (output :: # Type) + = ChartBuilder (Builder (Record input) (Record output)) +``` + +これら`ChartBuilder`の2つを一緒にするには、出力する新しい`ChartBuilder`に有効な `appliesTo`パラメータが有効であるかを確認する必要があります。そのため、 `( a :: _ , b :: _ )`と `( a :: _ )`の構成を考えると、出力`( a :: _ )`を生成する必要があります。 + +Union制約のシステムを表現するために複数の制約を同時に解決できるのであれば便利ですが、一度に1つの制約しか完全に決定できないため、RowToListに関して実装する必要があります。この問題については、"PureScriptで異なる型のレコードの差分を取る"という記事で詳しく書かれていますので、時間があれば読んでください。 + + + +--- + +Thanks to @noolbar for the Japanese translation! diff --git a/advent-2018/README.md b/advent-2018/README.md new file mode 100644 index 0000000..2d40beb --- /dev/null +++ b/advent-2018/README.md @@ -0,0 +1,39 @@ +This is a copy of , made for me to be able to grep through my writings. + +# Topics + + 1. (Lib) Simple-JSON + 2. (Tutorial) Psc-Package + 3. (Symbol) Tanghulu + 4. (Nix) Easy PureScript nix + 5. (Types) Type classes are pattern matching for types + 6. (Row) Home Run Ball + 7. (Tutorial) Spacchetti + 8. (Symbol) Type level path params + 9. (Nix) Psc-Package2Nix +10. (Types) Oh-Yes, datatype generic based +11. (Lib) Inferred record types with Simple-JSON +12. (Row) Tortellini +13. (Symbol) Type annotated type level string templates +14. (Tutorial) Implementing your own compiler type class +15. (Nix) Bower to nix +16. (Lib) Dynamically typed errors in purescript +17. (Row) Union Constraints +18. (Symbol) Jajanmen +19. (Tutorial) FFI in purescript +20. (Nix) Pandoc with Nix +21. (Types) Instance chains and Kazunoko +22. (Row) Naporitan +23. (Types) Inferred error types +24. (Row) RowToList Chart.js +25. (Final) Closing thoughts + +categories: + +* row: 5 +* tutorial: 4 +* nix: 4 +* symbol: 4 +* types: 4 +* lib: 3 +* final: 1