Skip to content

Commit 5f1ba9f

Browse files
Implement Data.Reflectable (#289)
* Implement Data.Reflectable * Use ordering type instead of the primitive kind * Add tests for reflection and reification Co-authored-by: Thomas Honeyman <[email protected]>
1 parent eff3517 commit 5f1ba9f

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Breaking changes:
1212
- Fix `Show` instance on records with duplicate labels by adding `Nub` constraint (#269 by @JordanMartinez)
1313

1414
New features:
15+
- Added the `Data.Reflectable` module for type reflection (#289 by @PureFunctor)
1516

1617
Bugfixes:
1718

src/Data/Reflectable.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// module Data.Reflectable
2+
3+
export const unsafeCoerce = function (arg) {
4+
return arg;
5+
};

src/Data/Reflectable.purs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
module Data.Reflectable
2+
( class Reflectable
3+
, class Reifiable
4+
, reflectType
5+
, reifyType
6+
) where
7+
8+
import Data.Ord (Ordering)
9+
import Type.Proxy (Proxy(..))
10+
11+
-- | A type-class for reflectable types.
12+
-- |
13+
-- | Instances for the following kinds are solved by the compiler:
14+
-- | * Boolean
15+
-- | * Int
16+
-- | * Ordering
17+
-- | * Symbol
18+
class Reflectable :: forall k. k -> Type -> Constraint
19+
class Reflectable v t | v -> t where
20+
-- | Reflect a type `v` to its term-level representation.
21+
reflectType :: Proxy v -> t
22+
23+
-- | A type class for reifiable types.
24+
-- |
25+
-- | Instances of this type class correspond to the `t` synthesized
26+
-- | by the compiler when solving the `Reflectable` type class.
27+
class Reifiable :: Type -> Constraint
28+
class Reifiable t
29+
30+
instance Reifiable Boolean
31+
instance Reifiable Int
32+
instance Reifiable Ordering
33+
instance Reifiable String
34+
35+
-- local definition for use in `reifyType`
36+
foreign import unsafeCoerce :: forall a b. a -> b
37+
38+
-- | Reify a value of type `t` such that it can be consumed by a
39+
-- | function constrained by the `Reflectable` type class. For
40+
-- | example:
41+
-- |
42+
-- | ```purs
43+
-- | twiceFromType :: forall v. Reflectable v Int => Proxy v -> Int
44+
-- | twiceFromType = (_ * 2) <<< reflectType
45+
-- |
46+
-- | twiceOfTerm :: Int
47+
-- | twiceOfTerm = reifyType 21 twiceFromType
48+
-- | ```
49+
reifyType :: forall t r. Reifiable t => t -> (forall v. Reflectable v t => Proxy v -> r) -> r
50+
reifyType s f = coerce f { reflectType: \_ -> s } Proxy
51+
where
52+
coerce
53+
:: (forall v. Reflectable v t => Proxy v -> r)
54+
-> { reflectType :: Proxy _ -> t }
55+
-> Proxy _
56+
-> r
57+
coerce = unsafeCoerce

test/Test/Main.purs

+28
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ module Test.Main where
33
import Prelude
44
import Data.HeytingAlgebra (ff, tt, implies)
55
import Data.Ord (abs, signum)
6+
import Data.Reflectable (reflectType, reifyType)
7+
import Prim.Boolean (True, False)
8+
import Prim.Ordering (LT, GT, EQ)
69
import Test.Data.Generic.Rep (testGenericRep)
710
import Test.Utils (AlmostEff, assert)
11+
import Type.Proxy (Proxy(..))
812

913
main :: AlmostEff
1014
main = do
@@ -15,6 +19,8 @@ main = do
1519
testIntDegree
1620
testRecordInstances
1721
testGenericRep
22+
testReflectType
23+
testReifyType
1824
testSignum
1925

2026
foreign import testNumberShow :: (Number -> String) -> AlmostEff
@@ -153,6 +159,28 @@ testRecordInstances = do
153159
(top :: { a :: Boolean }).a
154160
== top
155161

162+
testReflectType :: AlmostEff
163+
testReflectType = do
164+
assert "reflectType: Symbol -> String" $ reflectType (Proxy :: _ "erin!") == "erin!"
165+
assert "reflectType: Boolean -> Boolean, True" $ reflectType (Proxy :: _ True) == true
166+
assert "reflectType: Boolean -> Boolean, False" $ reflectType (Proxy :: _ False) == false
167+
assert "reflectType: Ordering -> Ordering, LT" $ reflectType (Proxy :: _ LT) == LT
168+
assert "reflectType: Ordering -> Ordering, GT" $ reflectType (Proxy :: _ GT) == GT
169+
assert "reflectType: Ordering -> Ordering, EQ" $ reflectType (Proxy :: _ EQ) == EQ
170+
assert "reflectType: Int -> Int, 42" $ reflectType (Proxy :: _ 42) == 42
171+
assert "reflectType: Int -> Int, -42" $ reflectType (Proxy :: _ (-42)) == -42
172+
173+
testReifyType :: AlmostEff
174+
testReifyType = do
175+
assert "reifyType: String -> Symbol" $ reifyType "erin!" reflectType == "erin!"
176+
assert "reifyType: Boolean -> Boolean, true" $ reifyType true reflectType == true
177+
assert "reifyType: Boolean -> Boolean, false" $ reifyType false reflectType == false
178+
assert "reifyType: Ordering -> Ordering, LT" $ reifyType LT reflectType == LT
179+
assert "reifyType: Ordering -> Ordering, GT" $ reifyType GT reflectType == GT
180+
assert "reifyType: Ordering -> Ordering, EQ" $ reifyType EQ reflectType == EQ
181+
assert "reifyType: Int -> Int, 42" $ reifyType 42 reflectType == 42
182+
assert "reifyType: Int -> Int, -42" $ reifyType (-42) reflectType == -42
183+
156184
testSignum :: AlmostEff
157185
testSignum = do
158186
assert "Clarifies what 'signum positive zero' test is doing" $ show (1.0/0.0) == "Infinity"

0 commit comments

Comments
 (0)