Elimination by `TypeError` constraint - haskell

I'd like to use a TypeError constraint to make a "non-instance" produce a more meaningful type error:
{-# LANGUAGE DataKinds, KindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
import GHC.TypeLits
import Data.Proxy
class Compat (x :: Bool) (y :: Bool) where
combine :: Proxy x -> Proxy y -> Int
instance Compat False pre2 where
combine _ _ = 42
instance Compat True False where
combine _ _ = 1
instance (TypeError (Text "Meaningful error message goes here")) => Compat True True where
combine = _
At the hole, I'd like to fill it using elimination by the TypeError constraint, i.e. use the fact that I have a TypeError constraint in scope to avoid having to write undefined or error or similar.
Is that possible?

I don't think this is possible with the standard TypeError, but you can define your own variant (TE below) so to provide the eliminator you need.
{-# LANGUAGE
DataKinds, UndecidableInstances,
MultiParamTypeClasses, KindSignatures, TypeFamilies #-}
import GHC.TypeLits
import Data.Kind
class Impossible where
impossible :: a
type family TE (t :: ErrorMessage) :: Constraint where
TE t = (TypeError t, Impossible)
class C t where
foo :: t -> Bool
instance (TE (Text "impossible")) => C Bool where
foo _ = impossible

Related

Type Family Not Evaluating

I am quite new to type level programming in haskell and I'm stuck with the following example. It is a fragment of a type level type checker for a small dsl. I want the Find type family to return a proof that a given element is contained in Env, or force a compile time error otherwise.
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE UndecidableInstances #-}
import GHC.TypeLits
-- atomic data types in the source language
data Atom = Boolean
-- the typechecking environment
type Env = [(Symbol, Atom)]
-- A proof that a particular pair has been declared in the Env
data Elem (n :: Symbol) (a :: Atom) (e :: Env) where
DH :: Elem n a ('(n,a):e)
DT :: Elem n a e -> Elem n a (t:e)
-- Compile time type env lookup
type Find :: Symbol -> Atom -> Env -> Elem n a e
type family Find n a e where
Find n a ('(n,a): e) = DH
Find n a ('(t,p): e) = DT (Find n a e)
Find n a '[] = TypeError (Text "name '" :<>: Text n :<>: Text "' not found in env")
However when I try to evaluate Find in ghci it seems to be stuck:
kind! Find "hi" Boolean '[ '("hi", Boolean) ]
Find "hi" Boolean '[ '("hi", Boolean) ] :: Elem n a e
I would expect this to reduce to DH :: Elem "hi" Boolean '[ '("hi", Boolean) ], but nothing seems to have happened. Have I somehow defined my type family incorrectly?

Why can I not use type variables in this instance declaration?

I'm trying to use TypeApplications to disambiguate between which instance of a type class I am calling. Unfortunately, it seems that an instance declaration's type parameters cannot be used in the instance body. Specifically, in this toy example:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
class Foo a where
foo :: String
instance Foo () where
foo = "()"
instance Foo Int where
foo = "Int"
class Bar b where
bar :: String
instance Foo a => Bar a where
bar = foo #a
will error with Not in scope: type variable 'a' at the last line. If I remove the type application, instead the error Could not deduce (Foo a0) from the context Foo a is given, which is reasonable, ass foo by itself is ambiguous.
Is there some way for me to access the type parameter, or otherwise coerce the compiler into recognising this?
A type variable can be used in an instance declaration, but only if it's scoped:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE TypeApplications, AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}
class Foo a where foo :: String
class Bar b where bar :: String
instance ∀ a . Foo a => Bar a where
bar = foo #a
As always, ∀ can also be written forall if you prefer ASCII.

Why does GHC think that the type variable is ambiguous for a class with a default implementation?

In the following example I would expect GHC to be able to constrain the variable a to type A because that's what I say in instance EAmbiguous A. However when loading this into ghci I get:
Ambiguous type variable ‘a0’ arising from a use of ‘Main.$dmcount’
The code in question:
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
import GHC.Generics (Rep, Generic)
import GHC.Base (Type)
class EAmbiguous a where
count :: Int
default count :: (Generic a, GenericAmbiguous (Rep a)) => Int
count = genericCount #(Rep a)
class GenericAmbiguous (a :: Type -> Type) where
genericCount :: Int
instance GenericAmbiguous (f p) where
genericCount = 10
data A = A deriving Generic
-- The error happens when defining a body-less instance:
-- • Ambiguous type variable ‘a0’ arising from a use of ‘Main.$dmcount’
-- prevents the constraint ‘(Generic a0)’ from being solved.
instance EAmbiguous A
I've been looking at this for a good while and I'm convinced that this should work, but I'm clearly missing something. So my question is: Why can't GHC solve a to A?
This turned out to be a GHC bug which is fixed in GHC 8.0.2: https://ghc.haskell.org/trac/ghc/ticket/12220

How to create a value in compdata

I've got the following compdata example code
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TemplateHaskell #-}
module Web.Rusalka.Hello (
iB, iV, Z) where
import Data.Comp
import Data.Comp.Ops
import Data.Comp.Show ()
import Data.Comp.Derive
data B e = B Bool
data V a e = V a
type Z a = Term (B :+: V a)
$(derive [makeFunctor, makeTraversable, makeFoldable,
makeEqF, makeShowF, smartConstructors, smartAConstructors,
makeArbitrary, makeArbitraryF]
[''B, ''V])
(You'll note that in fact, everything in Z is a leaf node.)
Now, as I understand it, this has created two functions, iB and iV, that can be used to create (Z a) s. However, I can't for the life of me figure
out how to create, for instance a (Z Int). What do I need to put in? (Or what am I misunderstanding?)
iB True :: Z Int or iV (1 :: Int) :: Z Int produce valid, printable expressions within this module.

Haskell Constraint Kinds - default constraint for default implementation

Headline: I would like to provide a default implementation for a class method parametrised over a constraint, which uses the default instance for that constraint.
Consider the following:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
import GHC.Exts (Constraint)
class Foo a where
type Ctx a :: Constraint
type Ctx a = Show a
foo :: (Ctx a) => a -> String
foo = show
main :: IO ()
main = putStrLn "Compiles!"
This fails to compile with the error of:
Could not deduce (Show a) arising from a use of ‘show’
from the context (Foo a)
From my perspective, it should be using the default constraint of Show, which would let this compile. Is there any reason this doesn't work, or can anyone suggest a good way to achieve this?
You can achieve this using DefaultSignatures:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DefaultSignatures #-}
import GHC.Exts (Constraint)
class Foo a where
type Ctx a :: Constraint
type Ctx a = Show a
foo :: (Ctx a) => a -> String
default foo :: Show a => a -> String
foo = show
main :: IO ()
main = putStrLn "Compiles!"
From my perspective, it should be using the default constraint of Show, which would let this compile.
The reason your approach doesn't work is that the user of your class should be able to override any number of defaults. Your code would break if someone tried to override Ctx but not foo.

Resources