-
Notifications
You must be signed in to change notification settings - Fork 150
Open
Description
Lets say I wanna have a bounded number type with Num capabilities, then I can get that for some fixed lower bound 0 and upper bound 9 as follows:
{-@ LIQUID "" @-}
{-# OPTIONS_GHC -fplugin=LiquidHaskell #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StandaloneKindSignatures #-}
module T2457 where
import GHC.Base (Type)
import GHC.TypeNats (Nat, Natural)
{-@ type BI010 = { i : Integer | i >= 0 && i < 10 } @-}
{-@ data T = T BI010 @-}
type T :: Type
data T = T Integer
{-@ inline t2i @-}
{-@ t2i :: t : T -> { i : BI010 | t == T i } @-}
t2i :: T -> Integer
t2i (T i) = i
{-# INLINE t2i #-}
t0 :: T
t0 = T 3
-- does not LH type check, as expected
--t1 :: T
--t1 = T 10
{-@
instance Num T where
(+) ::
a : T ->
{ b : T | t2i a + t2i b < 10 } ->
{ t : T | t2i t == t2i a + t2i b }
(-) ::
a : T ->
{ b : T | t2i a >= t2i b } ->
{ t : T | t2i t == t2i a - t2i b }
(*) ::
a : T ->
{ b : T | t2i a * t2i b < 10 } ->
{ t : T | t2i t == t2i a * t2i b }
negate :: T -> T
abs :: T -> T
signum :: T -> T
fromInteger ::
i : BI010 ->
{ t : T | t2i t == i }
@-}
instance Num T where
a + b = T $ t2i a + t2i b
a - b = T $ t2i a - t2i b
a * b = T $ t2i a * t2i b
negate = undefined
abs = id
signum = undefined
fromInteger = T
t2 :: T
t2 = 3 + 3
-- does not LH type check, as expected
--t3 :: T
--t3 = 3 + 8LH checks the bounds in this case as expected.
However, if I parameterize the bound using a type level natural instead, e.g.
{-@ LIQUID "" @-}
{-@ embed Natural as Int @-}
{-@ type BoundedInteger N = { i : Integer | N > 0 && i >= 0 && i < N } @-}
{-@ data P n = P Integer @-}
{-@ P :: forall (n :: Nat). BoundedInteger n -> P n @-}
type P :: Nat -> Type
data P n = P Integer
{-@ inline p2i @-}
{-@ p2i :: forall (n :: Nat). p : P n -> { i : BoundedInteger n | p == P i } @-}
p2i :: P n -> Integer
p2i (P i) = i
{-# INLINE p2i #-}
p0 :: P 10
p0 = P 3
-- does not LH type check, as expected
--p1 :: P 10
--p1 = P 10
{-@
instance Num (P n) where
(+) ::
forall (n :: Nat).
a : P n ->
{ b : P n | p2i a + p2i b < n } ->
{ x : P n | p2i x == p2i a + p2i b }
(-) ::
forall (n :: Nat).
a : P n ->
{ b : P n | p2i a >= p2i b } ->
{ x : P | p2i x == p2i a - p2i b }
(*) ::
forall (n :: Nat).
a : P n ->
{ b : P n | p2i a * p2i b < n } ->
{ x : P | p2i x == p2i a * p2i b }
negate :: forall (n :: Nat). P n -> P n
abs :: forall (n :: Nat). P n -> P n
signum :: forall (n :: Nat). P n -> P n
fromInteger ::
forall (n :: Nat).
i : BoundedInteger n ->
{ x : P n | p2i x == i }
@-}
instance Num (P n) where
a + b = P $ p2i a + p2i b
a - b = P $ p2i a - p2i b
a * b = P $ p2i a * p2i b
negate = undefined
abs = id
signum = undefined
fromInteger = P
p2 :: P 10
p2 = 3 + 3
-- LH SAFE, but 3 + 9 is no refinement in P 10!
p3 :: P 10
p3 = 3 + 9then the last definition of p3 LH type checks, although it shouldn't. Note that only change here is the additionally introduced type level nat parameter.
I'd expect the parameterized version to behave the same as the non-parameterized one in this case.
Tested with GHC 9.10 and 8c550df.
2024-12-17: Update of the reproducer to work with 791567f.
Metadata
Metadata
Assignees
Labels
No labels