commit 375ae8f19c95cb47bd5133660135ed952e8e7f93
parent 6d41b7f3f1653a2c17a9b7e4bea1ea2e26d959cd
Author: Robert Russell <robert@rr3.xyz>
Date: Wed, 16 Sep 2026 10:52:15 -0700
Haskell: add Functional# class for representation-genericity
Diffstat:
4 files changed, 68 insertions(+), 14 deletions(-)
diff --git a/src/Lude/Haskell.hs b/src/Lude/Haskell.hs
@@ -7,6 +7,7 @@ import GHC.Exts as X (IsList (..), IsString (..))
import Lude.Haskell.Category as X -- TODO: Probably hide some stuff from here.
import Lude.Haskell.Constraint as X
import Lude.Haskell.Equality as X
+import Lude.Haskell.Functional as X
import Lude.Haskell.Kind as X
import Lude.Haskell.Repr as X (Repr)
import Lude.Haskell.Type as X
diff --git a/src/Lude/Haskell/Functional.hs b/src/Lude/Haskell/Functional.hs
@@ -0,0 +1,61 @@
+module Lude.Haskell.Functional (
+ Lev,
+ Functional# (..),
+ lam2#,
+ app2#,
+) where
+
+import Data.Tuple.Experimental qualified as GHC
+
+import Lude.Haskell.Constraint
+import Lude.Haskell.Repr
+import Lude.Haskell.Repr qualified as Repr
+import Lude.Haskell.Type
+
+--------------------------------------------------------------------------------
+
+-- | Transparently box a type of any representation
+--
+-- Use `Lev` to declare unlifted values at the top-level. For example, for any
+-- @r :: Repr@, @A :: Type r@, and @bar :: A@:
+-- @
+-- foo :: Lev A
+-- foo = bar
+-- @
+type Lev :: forall {r}. Type @r -> Type
+type Lev a = (GHC.CTuple0) => a
+
+type Functional# :: Repr -> Constraint
+class Functional# r where
+ lam# :: forall (a :: Type @r) {s} (b :: Type @s). (Lev a -> b) -> (a -> b)
+ app# :: forall (a :: Type @r) {s} (b :: Type @s). (a -> b) -> (Lev a -> b)
+
+lam2#
+ :: forall {r :: Repr} (a :: Type @r) (b :: Type @r) {s} (c :: Type @s)
+ . (Functional# r) => (Lev a -> Lev b -> c) -> (a -> b -> c)
+lam2# f = lam# \x -> lam# (f x)
+
+app2#
+ :: forall {r :: Repr} (a :: Type @r) (b :: Type @r) {s} (c :: Type @s)
+ . (Functional# r) => (a -> b -> c) -> (Lev a -> Lev b -> c)
+app2# f x y = f `app#` x `app#` y
+
+--------------------------------------------------------------------------------
+
+instance Functional# Repr.BoxL where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.BoxU where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Addr where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Int where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Int8 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Int16 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Int32 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Int64 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Nat where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Nat8 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Nat16 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Nat32 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Nat64 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Flt32 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+instance Functional# Repr.Flt64 where lam# f = \x -> f x; {-# INLINE lam# #-}; app# f = \x -> f x; {-# INLINE app# #-}
+
+-- TODO: Tuple and Sum instances? There're infinitely many :(
diff --git a/src/Lude/Haskell/Kind.hs b/src/Lude/Haskell/Kind.hs
@@ -14,12 +14,16 @@ import Lude.Haskell.Repr qualified as Repr
type Kind = GHC.TYPE Repr.BoxL
+-- NOTE: We typically avoid conflating types and kinds,
+-- but when we do conflate them, we use KindOrType.
+type KindOrType r = GHC.TYPE r
+
-- | The kind of "invisibly" `Repr`-generic types and constraints
type KindR :: Kind
type KindR = forall (r :: Repr). Kind
-type Endo :: Kind -> Kind
+type Endo :: forall (r :: Repr). KindOrType r -> Kind
type Endo i = i -> i
-type Biendo :: Kind -> Kind
+type Biendo :: forall (r :: Repr). KindOrType r -> Kind
type Biendo i = i -> i -> i
diff --git a/src/Lude/Haskell/Type.hs b/src/Lude/Haskell/Type.hs
@@ -1,7 +1,6 @@
module Lude.Haskell.Type (
Type,
Type1,
- Lev,
Tuple#,
Tuple,
) where
@@ -21,17 +20,6 @@ type Type @r = GHC.TYPE r
type Type1 :: Kind -> KindR
type Type1 i @r = i -> Type @r
--- | Transparently box a type of any representation
---
--- Use `Lev` to declare unlifted values at the top-level. For example, for any
--- @r :: Repr@, @A :: Type r@, and @bar :: A@:
--- @
--- foo :: Lev A
--- foo = bar
--- @
-type Lev :: forall {r}. Type @r -> Type
-type Lev a = (GHC.CTuple0) => a
-
--------------------------------------------------------------------------------
-- Tuples