question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Phantom types are a proposal to add types that have no runtime values. Such types are useful in several scenarios:

  • Following Curry-Howard, we can model propositions as types and terms as their proofs. If a proposition is represented by a phantom type, the proofs corresponding to it are erased at runtime.
  • We can model capabilities as unforgeable values of types. If the types are phantom types, the capabilities are purely static, they need not be passed at runtime.

Phantom types have a prototype implementation #1408. This implementation has evolved quite a bit over time. The present issue describes the latest state of the proposal which arose from discussions between @nicolasstucki, myself and other members at LAMP. It is still vague in several places and there are some issues left open.

The proposal introduces a new predefined class scala.Phantom, defined as follows

package scala
class Phantom {

  sealed abstract trait Any
  final abstract trait Nothing extends Any
  
  protected def assume[T <: Any]: T
  
  class Function1[-T <: Any, +R <: Any] {
    def apply(x: T): R
  }
  
  class ImplicitFunction1[-T <: Any, +R <: Any] extends Function1[T, R] {
    def apply(implicit x: T): R
  }
  
  ... same for other function arities ...
}

This class is not given in source but has to be synthesized in the compiler. Phantom is special in that Phantom.Any and Phantom.Nothing live completely outside the standard type hierarchy. They are not subtypes of scala.Any nor supertypes of scala.Nothing. A type is called phantom type if it extends p.Any where p is an instance of class Phantom.

Definition: All types that are supertypes of scala.Nothing and subtypes of scala.Any make up the standard type domain. If p is an instance of class Phantom, then all types that are supertypes of p.Nothing and subtypes of p.Any make up the phantom type domain of p.

There are some rules to observe:

  • Type bounds cannot be mixed between different type domains.
  • Intersections (&) and unions (|) cannot be formed between different type domains.

Since p.Any is sealed, it is impossible to define a class that extends it. Since it is abstract, one cannot create instances of it. Likewise, one can neither instantiate nor extend p.Nothing. It follows that no new classes can be defined in a phantom domain, and no objects of phantom types can be created using new. However, one can define new phantom types as abstract types with phantom type bounds or as aliases of phantom types.

Every type parameter and abstract type has (possibly implied) bounds. These can be either phantom or non-phantom types. It follows that it is not possible to have a generic type parameter that ranges over both phantom and non-phantom types, nor can such a parameter range over phantom types from different domains.

If p is an instance of Phantom, the expression p.assume[T] synthesizes a value of phantom type T. This is the only way to produce a value of a phantom type. The actual implementation of assume is irrelevant because all phantom types are erased. That is,

  • All value definitions or parameters of phantom types are deleted.
  • All method definitions returning phantom types are deleted. It should be checked that such methods are pure, i.e. they do not have observable side effects.
  • All expressions of phantom types are deleted. It’s an open question whether expressions of phantom types must be pure; if they are not, the side-effecting parts of such expressions have to be lifted out and executed.

We discussed a lot about the nature of assume. If phantom types model a theory then assume defines axioms of that theory. If they define capabilities, then assume defines capabilities given a priori. Either way, adding assumes in arbitrary places destroys the soundness of the theory or the safety guarantees of the capability system. So assume needs to be carefully controlled.

Note that assume is protected in class Phantom, This means that individual phantom domains can each implement their own policy to what degree assume should be made available to clients. A phantom domain could either hide assume completely, and only export some fixed axioms or capabilities that are defined in terms of it. Or it could export assume - either unchanged or under a different name such as uncheckedAssume.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:14 (11 by maintainers)

github_iconTop GitHub Comments

4reactions
oderskycommented, Feb 28, 2017

One other question is whether there should be just one phantom domain or several. If we want to integrate perishable capabilities (aka effects) we might need another phantom domain for them where we are not allowed to capture values of such types at all. It could also be useful for other purposes to have several completely isolated phantom domains. A simple way to achieve that would be to change the object definition of Phantom to a class which can be inherited.

1reaction
Blaisorbladecommented, Mar 1, 2018

@joan38 TL;DR IIUC: With unused parameters you can mark values of arbitrary types to be erased at runtime, instead of having to implement separate phantom types. I don’t have a link though yet.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Phantom type - HaskellWiki
A phantom type is a parameterised type whose parameters do not all appear on the right-hand side of its definition, e.g. from Control....
Read more >
Phantom types in Swift - Swift by Sundell
Using phantom types is an incredibly powerful technique that can let us leverage the type system in order to validate different variants of...
Read more >
Phantom types in Swift | Swift with Majid
A phantom type is a generic type that is declared but never used inside a type where it is declared. It is usually...
Read more >
How to use phantom types in Swift – Hacking with Swift+
Phantom types are a powerful way to give the Swift compiler extra information about our code so that it can stop us from...
Read more >
Three Use Cases of Phantom Types - kean.blog
Phantom types are used to parameterize generic types but never actually appear in their implementation. Why are they useful?
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found