Skip to content

Latest commit

 

History

History
119 lines (80 loc) · 3.7 KB

0000-code-texp.rst

File metadata and controls

119 lines (80 loc) · 3.7 KB

Make Q (TExp a) into a newtype ==============

Leave blank. This will be filled in when the proposal is accepted.

#16177

Leave blank. This will be filled in with the first GHC version which implements the described feature.

haskell

This proposal is discussed at this pull request. After creating the pull request, edit this file again, update the number in the link, and delete this bold sentence.

I propose to modify the typed template Haskell API to make the Code type more abstract. In particular we introduce a new data type called Code which is abstract such that Code a represents an expression of type a.

Motivation

There are three problems with the current API:

  1. Q (TExp a) leaks implementation details to the user. Using effects from Q means that splices can fail to run which goes against the principle of typed staged programming. The principle is that we should never fail when running a splice. Errors should occur whilst constructing the value that we want to splice in so that we ensure that we can only construct well-scoped and well-typed terms.

In particular at least qAddTopDecls, qRunIO and qReport are all unsafe in this manner as they cause errors to happen whilst splicing an expression.

Making Code abstract means that users have to explicitly opt into this unsafe behaviour.

  1. It is hard to properly write instances for Q (TExp a) as its the composition of two data types. Doing so in your program involves making your own newtype and doing a lot of wrapping/unwrapping.

    For example, if I want to create a language which I can either run immediately or generate code from I could write the following with the new API. :

    class Lang r where
      _int :: Int -> r Int
      _if  :: r Bool -> r a -> r a -> r a
    
    instance Lang Identity where
      _int = Identity
      _if (Identity b) (Identity t) (Identity f) = Identity (if b then t else f)
    
    instance Lang Code where
      _int = liftTyped
      _if cb ct cf = [|| if $$cb then $$ct else $$cf ||]
  2. Q (TExp a) is ugly to read and write.

Proposed Change Specification

A newtype is defined called Code.:

newtype Code a = Code (Q (TExp a))

There are three constructs that the proposal affects.

Quoting an expression e :: T now produces an expression of typed Code T.:

-- foo :: Q (TExp Int)
foo :: Code Int
foo = [|| 5 ||]

Splicing requires an expression of type Code T and produces a value of type T.:

bar :: Int
bar = $$foo

The return type of lifting a value is changed from Q (TExp a) to Code a.:

class Lift a where
  liftTyped :: a -> Code a

A new method is added in order to perform unsafe Q actions inside of Code.:

unsafeQ :: Q (TExp a) -> Code a
unsafeQ = Code

Effect and Interactions

The proposal solves both problems as now users must use unsafeQ in order to escape into the Q monad. It is also straightforward to write instances because Code is a standard data type.

Costs and Drawbacks

The main drawback is that this will break all users of typed template Haskell who write type signatures. However, I feel like I am the only user so the impact will be minimal.

Alternatives

I can't think of any apart from not doing anything.

Unresolved Questions

Implementation Plan

Implementation is straightforward.