{"id":17968375,"url":"https://github.com/travisbrown/type-provider-examples","last_synced_at":"2025-03-25T09:30:48.278Z","repository":{"id":15515466,"uuid":"18249752","full_name":"travisbrown/type-provider-examples","owner":"travisbrown","description":"Macro-based type providers for Scala (examples)","archived":false,"fork":false,"pushed_at":"2015-08-31T13:38:10.000Z","size":504,"stargazers_count":85,"open_issues_count":2,"forks_count":12,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-20T00:39:55.873Z","etag":null,"topics":["scala","type-providers"],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/travisbrown.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-29T20:10:47.000Z","updated_at":"2022-11-30T10:05:45.000Z","dependencies_parsed_at":"2022-08-25T19:10:53.717Z","dependency_job_id":null,"html_url":"https://github.com/travisbrown/type-provider-examples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travisbrown%2Ftype-provider-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travisbrown%2Ftype-provider-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travisbrown%2Ftype-provider-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travisbrown%2Ftype-provider-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/travisbrown","download_url":"https://codeload.github.com/travisbrown/type-provider-examples/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245435044,"owners_count":20614817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["scala","type-providers"],"created_at":"2024-10-29T14:21:09.559Z","updated_at":"2025-03-25T09:30:48.241Z","avatar_url":"https://github.com/travisbrown.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"Scala type provider examples\n============================\n\n[![Build status](https://img.shields.io/travis/travisbrown/type-provider-examples/master.svg)](http://travis-ci.org/travisbrown/type-provider-examples)\n\nThis repository contains type provider examples that were discussed by\n[Eugene Burmako](https://twitter.com/xeno_by) and [Travis\nBrown](https://twitter.com/travisbrown) at the [2014 Scalar\nConference](http://scalar-conf.com/). The talk was not recorded, but our slides\nare available in [this repository](https://github.com/travisbrown/type-provider-examples/tree/master/docs).\n\nA type provider is a compile-time metaprogramming component that allows the\nuser to generate types (and implementations) from an external schema or other\ninformation source—you can think of them as a more principled solution to\nproblems that would traditionally be solved with textual code generation (or a\nlot of repetitive boilerplate). For more background information, see the\nfollowing resources:\n\n- [Type providers in Scala](http://docs.scala-lang.org/overviews/macros/typeproviders.html)\n- [Macro-supported DSLs for schema bindings in Scala](http://meta.plasm.us/posts/2013/06/19/macro-supported-dsls-for-schema-bindings/)\n- [Type providers in F#](http://msdn.microsoft.com/en-us/library/hh156509.aspx)\n- [Creating a type provider in F#](http://msdn.microsoft.com/en-us/library/hh361034.aspx)\n- [Dependent type providers](http://itu.dk/people/drc/pubs/dependent-type-providers.pdf)\n\nType providers are made possible in Scala by the experimental macro system\nintroduced in 2.10; neither of the approaches outlined here will work on\nearlier versions.\n\nAfter some conversations at the Scalar conference, we also began work on a\n[regular expression type provider](https://github.com/travisbrown/expressier)\nfor Scala. This project is currently a fairly rough proof of concept, but it\nmay help to show the range of possibilities for these kinds of components.\n\nMotivation\n----------\n\nWe'll take as a running example the construction of RDF graphs using\n[Banana RDF](https://github.com/w3c/banana-rdf), a Scala RDF library\ndeveloped by the [World Wide Web Consortium](http://www.w3.org/).\n\nBanana provides a clear and concise embedded DSL for building RDF graphs.\nFor example, we might describe the second draft notebook of Mary Shelley's\n_Frankenstein_ as follows:\n\n``` scala\nval frankensteinNotebookB = (\n  URI(\"http://shelleygodwinarchive.org/data/ox/ox-ms_abinger_c57\")\n    .a(dct.Text)\n    -- dc.title -\u003e- \"Frankenstein Draft Notebook B\"\n    -- dc.creator -\u003e- URI(\"https://en.wikipedia.org/wiki/Mary_Shelley\")\n)\n```\n\n`dct` and `dc` here are `org.w3.banana.Prefix` objects that represent the\n[Dublin Core Metadata Initiative](http://dublincore.org/)'s types and terms\nvocabularies.\n\nThe following is an example of how you'd define a `Prefix` class in Banana:\n\n``` scala\nclass DCPrefix[Rdf \u003c: RDF](implicit ops: RDFOps[Rdf])\n  extends PrefixBuilder(\"dc\", \"http://purl.org/dc/terms/\")(ops) {\n  val title = apply(\"title\")\n  val creator = apply(\"creator\")\n  // and so on...\n}\n```\n\nAnd then somewhere else in our project we'd write the following:\n\n``` scala\nobject dc extends DCPrefix[Rdf]\n```\n\nWhich would allow the usage above.\n\nThis isn't too bad, but in some cases our vocabularies can be quite large (the\nDublin Core terms vocabulary defines 77 properties, for example), which can make manually\ncreating `Prefix` classes inconvenient and error-prone. Creating these classes\nmanually can be especially frustrating when we have access to a\nmachine-readable description of the vocabulary in the form of an [RDF\nSchema](http://www.w3.org/TR/rdf-schema/). The terms schema, for example, is\n[published on the web](http://dublincore.org/2008/01/14/dcterms.rdf) by the\nDCMI under a Creative Commons license.\n\nType providers allow us to avoid the boilerplate of translating these schemas\ninto `Prefix` class definitions manually. This repository includes two\nmacro-based implementations of type providers in Scala, one demonstrating the\n\"public\" approach, in which the body of a publicly-visible class is provided\nby the macro, and the other demonstrating the \"anonymous\" approach, where the\nmacro defines and instantiates an anonymous class that is visible to the rest\nof the program as a structural type.\n\nAnonymous type providers\n------------------------\n\nThe anonymous approach is supported by `def` macros, which means that it can be\nused in Scala 2.10 without additional compiler plugins (although note that the\nexample implementation provided here uses quasiquotes and therefore does require\nthe [Macro Paradise plugin](http://docs.scala-lang.org/overviews/macros/paradise.html)).\n\nThe syntax looks quite natural—we simply call a method with some arguments\n(in this case a single argument—the path to the schema resource).\n\n``` scala\nval dct = PrefixGenerator.fromSchema[Rdf](\"/dctype.rdf\")\n\nval dc = PrefixGenerator.fromSchema[Rdf](\"/dcterms.rdf\")\n```\n\nThe macro does impose some additional constraints on the user of the type\nprovider, however. In particular, the path argument must be a string literal,\nand it must point to a valid RDF Schema resource on the build classpath. If\neither of these constraints is not satisfied, the type provider will fail with\na compile-time error.\n\nThe inferred types of `dct` and `dc`  in this example are structural types\nthat allow the usage demonstrated in the definition of `frankensteinNotebookB`\nabove. More specifically, the generated code for the second line will look\nsomething like the following:\n\n``` scala\nval dc = {\n  class Prefix2 extends PrefixBuilder(\"dc\", \"http://purl.org/dc/terms/\") {\n    val title = apply(\"title\")\n    val creator = apply(\"creator\")\n    // and so on...\n  }\n  new Prefix2 {}\n}\n```\n\nI.e., we're defining a class inside a block and then instantiating an\nanonymous subclass of that class (see [this Stack Overflow\nanswer](http://stackoverflow.com/a/18485004/334519) and [this earlier\nquestion](http://stackoverflow.com/q/14370842/334519) for some discussion of\nwhy this two-step process is necessary). We can't see the class itself outside\nof the block, but we can see its methods on the structural type that will be\ninferred for `dc`.\n\nNote that the type provider has also inferred the schema URI from the RDF\nSchema file (the second argument to the `PrefixBuilder` constructor) and has\npicked a reasonable short name for the `Prefix` (the first argument).\n\nPlease see the comments in [the\nimplementation](https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-anonymous/src/main/scala/anonymous/PrefixGenerator.scala)\nfor more detail about how exactly this approach works.\n\nPublic type providers\n---------------------\n\nThe public approach uses [macro annotations](http://docs.scala-lang.org/overviews/macros/annotations.html),\nwhich allow us to expand the body of an annotated object definition.\n\n``` scala\n@fromSchema(\"/dctype.rdf\") object dct extends PrefixBuilder[Rdf]\n\n@fromSchema(\"/dcterms.rdf\") object dc extends PrefixBuilder[Rdf]\n```\n\nThese definitions support the same usage as the anonymous examples above,\nbut `dct` and `dc` are full-fledged objects, not instances of structural types.\nThe generated code looks fairly similar:\n\n``` scala\nobject dc extends PrefixBuilder[Rdf](\"dc\", \"http://purl.org/dc/terms/\") {\n  val title = apply(\"title\")\n  val creator = apply(\"creator\")\n  // and so on...\n}\n```\n\nThe [implementation](https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-public/src/main/scala/public/PrefixGenerator.scala)\nis also pretty similar to the [anonymous type provider\nimplementation](https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-anonymous/src/main/scala/anonymous/PrefixGenerator.scala).\n\nVampire methods\n---------------\n\nOne of the disadvantages of using structural types in Scala is that they involve\nreflective access, which means you have to deal with warnings (and a hit to\nperformance). For example, when you compile the example project you'll see the\nfollowing:\n\n```\n[warn] ...reflective access of structural type member value title should be enabled\n[warn] by making the implicit value scala.language.reflectiveCalls visible.\n[warn]       -- dc.title -\u003e- \"Frankenstein Draft Notebook B\"\n[warn]             ^\n```\n\nIt's possible, however, to use [\"vampire\nmethods\"](http://meta.plasm.us/posts/2013/07/12/vampire-methods-for-structural-types/)\nto avoid this penalty. Vampire methods are macro methods on the anonymous\nclass that read their values from some location at compile time (in this case\nwe're using a static annotation on the method; if this sounds confusing,\nthat's because it is).\n\n(Note that in Scala 2.10.4 the use of vampire methods will still result in a\nreflective access warning, but this [has been fixed in\n2.10.5](https://github.com/scala/scala/pull/3602).)\n\nWhile we provide [an implementation](https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-anonymous/src/main/scala/anonymous/VampiricPrefixGenerator.scala)\nof our example using vampire methods here, in general it's probably better to\navoid the added complexity, unless you know for a fact that the performance of\ncalls to methods on the structural type is a problem in your application.\n\nLicenses\n--------\n\nPortions of this software may use RDF schemas copyright © 2011\n[DCMI](http://dublincore.org/), the Dublin Core Metadata Initiative.\nThese are licensed under the [Creative Commons 3.0\nAttribution](http://creativecommons.org/licenses/by/3.0/) license.\n\nAll other code is released under the [Apache License, Version\n2.0](http://www.apache.org/licenses/LICENSE-2.0.html).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftravisbrown%2Ftype-provider-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftravisbrown%2Ftype-provider-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftravisbrown%2Ftype-provider-examples/lists"}