-
Notifications
You must be signed in to change notification settings - Fork 5
/
Sec2_2_2_the.lhs
152 lines (128 loc) · 4.63 KB
/
Sec2_2_2_the.lhs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|Markdown version of this file: https://github.com/rpeszek/IdrisTddNotes/wiki/Part1_Sec2_2_2_the
|Idris Src: Sec2_3_3.idr
Section 2.2.2. Idris `the` function vs Haskell vs JVM
=====================================================
Idris code example
------------------
Idris Prelude comes with a very cool `the` function reimplemented here as `the'`.
This function allows to resolve polymorphic or overloaded data constructors:
|IdrisRef: Sec2_2_2_the.idr
idris repl:
```
*src/Part1/Sec2_2_2_the> the' Double 3
3.0 : Double
*src/Part1/Sec2_2_2_the> the' Int 3
3 : Int
*src/Part1/Sec2_2_2_the> the' Int "Test"
(input):1:1-15:When checking an application of function Part1.Sec2_2_2_the.the':
Type mismatch between
String (Type of "Test")
and
Int (Expected type)
*src/Part1/Sec2_2_2_the> the' (List Int) [1,2,3]
[1, 2, 3] : List Int
*src/Part1/Sec2_2_2_the> the' (List _) [1,2,3]
[1, 2, 3] : List Integer
*Part1/Sec2_2_2_the> the' (List Int) (1 :: 2 :: Nil)
[1, 2] : List Int
*src/Part1/Sec2_2_2_the> :module Data.Vect
*src/Part1/Sec2_2_2_the *Data/Vect> the' (Vect _ _) [1,2,3]
[1, 2, 3] : Vect 3 Integer
*Part1/Sec2_2_2_the *Data/Vect> the' (Vect 2 Int) (1 :: 2 :: Nil)
[1, 2] : Vect 2 Int
```
Compared to Haskell
-------------------
To do something similar, Haskell would need a value level representation of types.
I have no hope to reproduce the `List` and `Vect` examples, since Haskell does not
allow for two different `Nil`/`[]` or two different `Cons`/`::`/`:` in
single lexical scope.
But I can play with polymorphic numeric literals.
> {-# LANGUAGE
> TypeApplications
> #-}
> {-# OPTIONS_GHC -fwarn-unused-imports #-}
> module Part1.Sec2_2_2_the where
> import Data.Proxy
>
> the :: Proxy a -> a -> a
> the _ x = x
>
> double :: Proxy Double
> double = Proxy
>
> int :: Proxy Int
> int = Proxy
>
> string :: Proxy String
> string = Proxy
>
> list :: Proxy a -> Proxy ([a])
> list _ = Proxy
>
> numList :: Num a => Proxy ([a])
> numList = Proxy
ghci:
```
*Part1.Sec2_2_2_the> :t the int 3
the intProxy 3 :: Int
*Part1.Sec2_2_2_the> :t the double 3
the doubleProxy 3 :: Double
*Part1.Sec2_2_2_the> :t the double "test"
<interactive>:1:17: error:
• Couldn't match expected type ‘Double’ with actual type ‘[Char]’
• In the second argument of ‘the’, namely ‘"test"’
In the expression: the double "test"
*Part1.Sec2_2_2_the> :t the (list int) [1,2,3]
the (list int) [1,2,3] :: [Int]
*Part1.Sec2_2_2_the> :t the numList [1,3,4]
the numList [1,3,4] :: Num a => [a]
```
(Vector equivalent not shown because I am just lazy, besides Haskell does not allow
ad-hoc overloaded names like Idris.)
This approach is clearly not as nice as Idris'.
I had to declare helper proxies which is ugly.
Using `TypeApplications` extension
----------------------------------
As it was pointed out to me on [reddit](https://www.reddit.com/r/haskell/comments/99toaz/tdd_with_idris_book_vs_haskell/) (thank you u/Potato44 and u/kurtel)
`TypeApplications` extension provides a way to use types at value level
expressions.
https://www.microsoft.com/en-us/research/uploads/prod/2018/06/tyvars-in-pats-haskell18-final.pdf
So I can just do this
> double4 = id @Double 4
JVM
---
Any language that can express types as first class values should be able to implement
`the`, only that function does not always make much sense.
Java comes to mind because it has the `Class<T>` type. But it is not so easy.
Here is Groovy console showing what happens in Groovy
```Groovy
groovy> static <T> T the(Class<T> tc, T t) { return t;}
groovy> (the(Double, 3)).getClass()
Result: class java.lang.Integer
```
I think, this can be blamed on Java type erasure and Groovy being weak on types.
Also, this is not a fair example since Java does not really have a concept of
polymorphic numeric literals (or ah-hoc name overloading that is similar to Idris).
If this was Java code, this would not compile `the(Double.class, 3)`,
neither would this `the(Long.class, 3)`, these would: `the(Integer.class, 3)` as well as `the(Object.class, 3)`.
Adding `@CompileStatic` annotation to Groovy code prevents compilation of the above code as well.
Scala
-----
The interesting bits here are scala implicits.
Scala `implicitly` or shapeless' `the` provides functionality somewhat equivalent
to Idris' `the`
```scala
scala> implicitly[Double](2)
res0: Double = 2.0
scala> implicitly[Int](2)
res1: Int = 2
scala> import shapeless._
import shapeless._
scala> the[Double](2)
res2: Double = 2.0
scala> the[Int](2)
res3: Int = 2
```
Scala comes with built-in implicits for some numerical conversions
https://github.com/scala/scala/blob/v2.12.6/src/library/scala/Int.scala#L474