Nó đã được tuyên bố rằng newtype T a = T (a -> Int)
là một kiểu constructor mà không phải là một functor (nhưng là một fravtor contravariant). Làm thế nào? Hoặc những gì là contravariant functor (whence tôi giả định nó sẽ được rõ ràng lý do tại sao điều này không thể được thực hiện một functor bình thường)?Hiển thị rằng `newtype T a = T (a -> Int)` là một kiểu Constructor không phải là một Functor
Trả lời
Với
newtype T a = T (a -> Int)
chúng ta hãy cố gắng xây dựng các ví dụ Contravariant
cho datatype này.
Đây là typeclass trong câu hỏi:
class Contravariant f where
contramap :: (a -> b) -> f b -> f a
Về cơ bản, contramap
là giống như fmap
, nhưng thay vì nâng một hàm a -> b
-f a -> f b
, nó nâng nó để f b -> f a
.
Hãy bắt đầu bằng văn bản cho ví dụ ...
instance Contravariant T where
contramap g (T f) = ?
Trước khi chúng tôi điền vào ?
, chúng ta hãy suy nghĩ về những gì các loại g
và f
là:
g :: a -> b
f :: b -> Int
Và cho rõ ràng, chúng tôi cũng có thể đề cập đến điều đó
f a ~ T (a -> Int)
f b ~ T (b -> Int)
Vì vậy, chúng ta có thể điền thông tin vào ?
như sau:
instance Contravariant T where
contramap g (T f) = T (f . g)
Để trở thành siêu pedantic, bạn có thể đổi tên g
như aToB
, và f
như bToInt
.
instance Contravariant T where
contramap aToB (T bToInt) = T (bToInt . aToB)
Lý do bạn có thể viết một ví dụ Contravariant
cho T a
nắm để thực tế là a
là ở vị trí contravariant trong T (a -> Int)
. Cách tốt nhất để thuyết phục bản thân rằng T a
không phải là Functor
là thử (và không thành công) để tự viết ví dụ Functor
.
Giả sử T là một hàm. Sau đó, chúng tôi có
fmap :: (a -> b) -> T a -> T b
Bây giờ, hãy thử triển khai.
instance Functor T where
fmap f (T g) = T $ \x -> _y
Rõ ràng chúng ta bắt đầu với một cái gì đó như thế này, và kết hợp các giá trị f
, g
, và x
bằng cách nào đó để tính toán giá trị cho y
đó là của đúng loại. Làm thế nào chúng ta có thể làm điều đó? Vâng, thông báo tôi đã gọi nó là _y
, mà nói với GHC tôi cần một số trợ giúp tìm ra những gì để đặt ở đó. GHC phải nói gì?
<interactive>:7:28: error:
• Found hole: _y :: Int
Or perhaps ‘_y’ is mis-spelled, or not in scope
• In the expression: _y
In the second argument of ‘($)’, namely ‘\ x -> _y’
In the expression: T $ \ x -> _y
• Relevant bindings include
x :: b (bound at <interactive>:7:23)
g :: a -> Int (bound at <interactive>:7:13)
f :: a -> b (bound at <interactive>:7:8)
fmap :: (a -> b) -> T a -> T b (bound at <interactive>:7:3)
Vì vậy, bây giờ chúng tôi đã rõ ràng về mọi loại có liên quan, phải không? Chúng tôi cần phải trả lại một Int
bằng cách nào đó, và những gì chúng ta phải xây dựng nó ra khỏi là:
x :: b
g :: a -> Int
f :: a -> b
Vâng, được rồi, điều duy nhất chúng tôi có mà có thể có thể tạo ra một Int
là g
, vì vậy hãy điền vào đó ở, rời g
's luận trống để hỏi GHC để được giúp đỡ nhiều hơn nữa:
instance Functor T where
fmap f (T g) = T $ \x -> g _y
<interactive>:7:31: error:
• Found hole: _y :: a
Where: ‘a’ is a rigid type variable bound by
the type signature for:
fmap :: forall a b. (a -> b) -> T a -> T b
at <interactive>:7:3
Or perhaps ‘_y’ is mis-spelled, or not in scope
• In the first argument of ‘g’, namely ‘(_y)’
In the expression: g (_y)
In the second argument of ‘($)’, namely ‘\ x -> g (_y)’
• Relevant bindings include
x :: b (bound at <interactive>:7:23)
g :: a -> Int (bound at <interactive>:7:13)
f :: a -> b (bound at <interactive>:7:8)
fmap :: (a -> b) -> T a -> T b (bound at <interactive>:7:3)
Được rồi, chúng ta có thể dự đoán này chính mình: gọi g
, chúng ta cần một giá trị kiểu a
từ đâu đó. Nhưng chúng tôi không có bất kỳ giá trị nào thuộc loại a
trong phạm vi và chúng tôi cũng không có bất kỳ hàm nào trả về giá trị loại a
! Chúng tôi đang mắc kẹt: không thể tạo ra giá trị của loại chúng tôi muốn bây giờ, mặc dù ở mỗi bước chúng tôi đã làm điều duy nhất có thể: không có gì chúng tôi có thể sao lưu và thử khác nhau.
Tại sao điều này lại xảy ra? Bởi vì nếu tôi cung cấp cho bạn chức năng loại a -> Int
và nói "nhưng bằng cách này, đây là chức năng từ a -> b
, vui lòng trả lại chức năng của tôi từ b -> Int
", bạn không thể thực sự sử dụng chức năng từ a -> b
, vì không ai bao giờ cung cấp cho bạn bất kỳ a
s để gọi nó! Nếu tôi đã cho bạn một hàm từ b -> a
thay vào đó, điều đó sẽ khá hữu ích, đúng không? Bạn có thể tạo ra một hàm từ b -> Int
sau đó, trước tiên, hãy gọi hàm b -> a
để nhận số a
và sau đó gọi hàm a -> Int
gốc để có được số Int
mong muốn.
Và đó là điều mà một hàm giả thuyết về: chúng tôi đảo ngược mũi tên trong hàm được chuyển đến fmap
, vì vậy nó có thể fmap trên những thứ bạn cần "(đối số chức năng) thay vì những thứ bạn" có "(giá trị cụ thể, trả lại giá trị của hàm, v.v.)
Bên cạnh đó, tôi đã tuyên bố trước đó rằng chúng tôi đã thực hiện "điều duy nhất" ở mỗi bước, đó là một chút của một lỗi. Chúng tôi không thể xây dựng một số Int
trong số f
, g
và x
, nhưng tất nhiên chúng tôi có thể tạo tất cả các loại số ra khỏi không khí.Chúng tôi không biết gì về loại b
, vì vậy chúng tôi không thể nhận được Int
có nguồn gốc từ nó theo một cách nào đó, nhưng chúng tôi chỉ có thể nói "hãy luôn trả về số không" và kỹ thuật này đáp ứng trình kiểm tra loại:
instance Functor T where
fmap f (T g) = T $ const 0
Rõ ràng điều này có vẻ khá sai, vì nó có vẻ như f
và g
nên được khá quan trọng và chúng tôi đang bỏ qua chúng! Nhưng nó kiểm tra loại, vì vậy chúng tôi ổn, phải không?
Không, điều này vi phạm một trong những luật functor:
fmap id = id
Chúng ta có thể chứng minh điều này một cách dễ dàng đủ:
fmap id (T $ const 5) = (T $ const 0) /= (T $ const 5)
Và bây giờ chúng ta thực sự có thử tất cả mọi thứ: cách duy nhất chúng ta có để xây dựng một số Int
mà không sử dụng loại b
của chúng tôi là loại bỏ mọi thứ, và tất cả những công dụng như vậy sẽ là đẳng cấu để sử dụng const
, sẽ vi phạm luật Functor.
Đây là một góc nhìn khác. Như liminalisht đã cho thấy, T
là Contravariant
.Những gì chúng ta có thể nói về các loại được cả hai biến thể và contravariant?
import Data.Void
change1, change1', change2 :: (Functor f, Contravariant f) => f a -> f b
change1 = contramap (const()) . fmap (const())
change1' = (() >$) . (() <$)
change2 = fmap absurd . contramap absurd
Hai triển khai đầu tiên là về cơ bản giống nhau (change1'
là một tối ưu hóa các change1
); mỗi người trong số họ sử dụng thực tế là ()
là một "đối tượng đầu cuối" của Hask. change2
thay vì sử dụng thực tế là Void
là một "đối tượng ban đầu".
Mỗi chức năng thay thế tất cả các a
s trong f a
với b
s mà không biết bất cứ điều gì nào về a
, b
, hoặc mối quan hệ giữa chúng, để lại mọi thứ khác như vậy. Có thể rõ ràng rằng điều này có nghĩa là f a
không thực sự phụ thuộc vào a
. Tức là, tham số của f
phải là ma. Đó là không phải là trường hợp cho T
, do đó, nó cũng không thể là biến thể.
- 1. Tại sao (a, a) không phải là một functor?
- 2. loại Scala: Class A không phải là tương đương với T trong đó T là: loại T = A
- 3. Làm (a, a) a Functor
- 4. Kiểu tham số T đang ẩn kiểu T trong <T> T [] toArray (T [] a) bằng Eclipse
- 5. Bắt một giá trị kiểu Integral a => [a] từ một giá trị của một Integral => ([a], [a], [a])
- 6. Đối với Func <T, TResult>, trong đó A mở rộng T, A không thỏa mãn đối với T
- 7. Bất kỳ sự khác biệt nào giữa t <'a> và 'a t in F #?
- 8. IEEE Std 754 Floating-Point: let t: = a - b, tiêu chuẩn có đảm bảo rằng a == b + t không?
- 9. Generic constructor (Lớp <T> clazz) không hỗ trợ T trong đó T là một Generics
- 10. Tại sao `[1," a "] :: [forall a. Hiển thị a => a] `không được phép?
- 11. std :: is_default_constructible <T> lỗi, nếu constructor là tin
- 12. Sự khác biệt giữa "loại A: B <C>" và "lớp A <T>: B <T> trong đó T: C"?
- 13. Có trường hợp sử dụng cho `newtype T = MkT (T -> T)` không?
- 14. "&" hoặc " ": sự khác biệt giữa A là gì <T kéo dài I1 & I2> và A <T kéo dài I1, I2>
- 15. const T {}; hoạt động, const T; thất bại khi T là một tổ chức phi POD,
- 16. Tại sao std :: is_base_of <T, T> đúng khi T là một loại lớp, nhưng sai khi T là một kiểu dựng sẵn?
- 17. Tại sao liên kết `T: 'a` bắt buộc để lưu trữ tham chiếu` &' a T`?
- 18. Ý nghĩa của `struct decay <T, R (A ..., ...)>`
- 19. Sự khác nhau giữa double a = a + int b và int a + = double b là gì?
- 20. Tại sao EnumMap <T> .keySet() trả về một Tập hợp <T>() và không phải là một EnumSet <T>()?
- 21. Sử dụng chức năng của `a` trên 'Newtype a`
- 22. Tên kiểu mẫu: R a b = Q (a -> (R a b, b))
- 23. Có phải Danh sách <T> là một danh sách được liên kết không?
- 24. Có phải <'a, 'b: 'a> có nghĩa là tuổi thọ 'b phải vượt xa tuổi thọ' a?
- 25. Sự khác biệt giữa <T extends A> void foo (Tt) và void foo (A a)
- 26. Triển khai IEquatable <T> khi T có thể là IEnumerable <T>
- 27. Ở đâu trong tiêu chuẩn C++ 11 nó cấm 'mẫu <typename T> lớp A {...}; mẫu <typename T> lớp A <int> {...}; ' (nếu ở đâu)?
- 28. Có phải Danh sách <T> trong đó T là một đại biểu ẩn danh có thể không?
- 29. Tại sao loại chức năng này (a -> a) -> a?
- 30. Loại ocaml 'a. 'a ->' có nghĩa là gì?