MyNixOS website logo
Description

Unified API for phantom typed newtypes and type aliases.

In scenarios where newtypes are not well supported, we may need to use type aliases instead for various reasons. This means we give away type safety, and static analysis tooling. We can get this safety back with Spooky. When we compile with -f typed, we get a type check using newtypes. When compiled without the flag we get the same code type checking with type aliases.

👻

Concept

This is a package designed to help optimize around scenarios with bad newtype support without losing type safety.

Spooky :: (s :: Type) -> (a :: Type) -> Type

If type a can be represented as type s, we can make s spooky. While we usually might use terms of type a, we need to represent a as a term of type s instead because of some tragedy. Converting our types to s directly loses important type information (knowledge of a is erased). We could preserve this information by placing a in a phantom typed newtype over s. But then we incuring cost around this newtype, that is not incurred by a type alias. However, if we use a type alias over s to get a into a phantom type, we get no additional safety over using s directly. Only readability is gained with the type alias.

This library resolves this tension

Data.Spooky.Spooky is a newtype or a type alias depending on cabal flags. This means you can develop with Spooky as a newtype and get all the benefits of the static type checking and static analysis in tooling, while for production use a type alias.

This technique was originally motivated by the need to reduce file sizes when using the Haskell to Plutus compiler in 2021.

The Typed Version

This is the default. You may pass a cabal flag -f typed for symmetry, but it wont do anything.

newtype Spooky (s :: Type) (a :: Type) = Spooky s

Note

Keep in mind with the typed version the following works. So we have some real safety:

 λ. newtype Typed a = Typed String deriving Show
 λ. typedId = id :: Typed Int -> Typed Int
 λ. y = Typed "wat" :: Typed String
 λ. typedId y

<interactive>:21:9: error:
    • Couldn't match type ‘[Char]’ with ‘Int’
      Expected type: Typed Int
        Actual type: Typed String
    • In the first argument of ‘typedId’, namely ‘y’
      In the expression: typedId y
      In an equation for ‘it’: it = typedId y

Untyped Version

This is not the default. Supplied along side the newtyped version, and can be enabled with a cabal flag.

cabal build -f untyped

will replace the newtype with an alias

type Spooky (s :: Type) (a :: Type) = s

This allows for some safety as we can hold type information in the phantom type representing the semantics of our code.

Note

Keep in mind the following works. So not that much safety is provided here:

 λ. type Untyped a = String
 λ. untypedId = id :: Untyped Int -> Untyped Int
 λ. x = "wat" :: Untyped String
 λ. untypedId x
"wat"
Metadata

Version

0.1.0.0

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows