Kiểu chữ Applicative
đại diện cho functor đơn hình lỏng lẻo bảo tồn cấu trúc đơn hình cartesian trên danh mục các hàm được gõ.
Nói cách khác, được đưa ra các đẳng cấu chính tắc chứng kiến (,)
tạo thành một cấu trúc đơn hình:
-- Implementations left to the motivated reader
assoc_fwd :: ((a, b), c) -> (a, (b, c))
assoc_bwd :: (a, (b, c)) -> ((a, b), c)
lunit_fwd :: ((), a) -> a
lunit_bwd :: a -> ((), a)
runit_fwd :: (a, ()) -> a
runit_bwd :: a -> (a, ())
Kiểu chữ và luật của nó có thể được viết tương tự như thế này:
class Functor f => Applicative f
where
zip :: (f a, f b) -> f (a, b)
husk :: () -> f ()
-- Laws:
-- assoc_fwd >>> bimap id zip >>> zip
-- =
-- bimap zip id >>> zip >>> fmap assoc_fwd
-- lunit_fwd
-- =
-- bimap husk id >>> zip >>> fmap lunit_fwd
-- runit_fwd
-- =
-- bimap id husk >>> zip >>> fmap runit_fwd
Người ta có thể tự hỏi một functor là oplax đơn hình đối với cùng cấu trúc có thể trông như thế nào:
class Functor f => OpApplicative f
where
unzip :: f (a, b) -> (f a, f b)
unhusk :: f () -> ()
-- Laws:
-- assoc_bwd <<< bimap id unzip <<< unzip
-- =
-- bimap unzip id <<< unzip <<< fmap assoc_bwd
-- lunit_bwd
-- =
-- bimap unhusk id <<< unzip <<< fmap lunit_bwd
-- runit_bwd
-- =
-- bimap id unhusk <<< unzip <<< fmap runit_bwd
Nếu chúng ta nghĩ về các loại liên quan đến các định nghĩa và luật, sự thật đáng thất vọng được tiết lộ; OpApplicative
không có ràng buộc cụ thể hơn Functor
:
instance Functor f => OpApplicative f
where
unzip fab = (fst <$> fab, snd <$> fab)
unhusk = const ()
Tuy nhiên, trong khi mọi Applicative
functor (thực sự, bất kỳ Functor
) là tầm thường OpApplicative
, không nhất thiết phải có một mối quan hệ tốt đẹp giữa sự Applicative
lỏng lẻo và sự OpApplicative
mờ nhạt. Vì vậy, chúng ta có thể tìm kiếm các functor đơn hình mạnh mẽ trong cấu trúc đơn hình cartesian:
class (Applicative f, OpApplicative f) => StrongApplicative f
-- Laws:
-- unhusk . husk = id
-- husk . unhusk = id
-- zip . unzip = id
-- unzip . zip = id
Luật đầu tiên ở trên là tầm thường, vì cư dân duy nhất của loại () -> ()
này là chức năng nhận dạng trên ()
.
Tuy nhiên, ba định luật còn lại, và do đó, chính lớp con, không tầm thường. Cụ thể, không phải mọi Applicative
trường hợp là một trường hợp hợp pháp của lớp này.
Dưới đây là một số Applicative
chức năng mà chúng tôi có thể tuyên bố các trường hợp hợp pháp về StrongApplicative
:
Identity
VoidF
(->) r
(xem câu trả lời)Monoid m => (,) m
Vec (n :: Nat)
Stream
(vô hạn)
Và đây là một số Applicative
s mà chúng ta không thể:
[]
Either e
Maybe
NonEmptyList
Mẫu ở đây gợi ý rằng StrongApplicative
lớp theo nghĩa là FixedSize
lớp, trong đó "kích thước cố định" * có nghĩa là bội số ** của cư dân a
trong một cư dân f a
là cố định.
Điều này có thể được nêu là hai phỏng đoán:
- Mỗi
Applicative
đại diện cho một thùng chứa "kích thước cố định" của các phần tử của đối số loại của nó là một thể hiện củaStrongApplicative
- Không có trường hợp
StrongApplicative
tồn tại trong đó số lần xuất hiệna
có thể thay đổi
Bất cứ ai cũng có thể nghĩ về các phản mẫu mà bác bỏ những phỏng đoán này, hoặc một số lý luận thuyết phục chứng minh tại sao chúng là đúng hay sai?
* Tôi nhận ra rằng tôi đã không xác định đúng tính từ "kích thước cố định". Thật không may, nhiệm vụ là một chút thông tư. Tôi không biết bất kỳ mô tả chính thức nào về một container "kích thước cố định" và tôi đang cố gắng đưa ra một mô tả. StrongApplicative
là nỗ lực tốt nhất của tôi cho đến nay.
Tuy nhiên, để đánh giá xem đây có phải là một định nghĩa tốt hay không, tôi cần một cái gì đó để so sánh nó với. Đưa ra một số định nghĩa chính thức / không chính thức về ý nghĩa của một functor có kích thước hoặc bội số cho trước đối với các cư dân của đối số kiểu của nó, câu hỏi đặt ra là liệu sự tồn tại của một StrongApplicative
thể hiện có phân biệt chính xác các hàm tử có kích thước cố định và khác nhau hay không.
Không nhận thức được một định nghĩa chính thức hiện có, tôi đang kêu gọi trực giác trong việc sử dụng thuật ngữ "kích thước cố định" của tôi. Tuy nhiên, nếu ai đó đã biết về một chủ nghĩa hình thức hiện có cho kích thước của một functor và có thể so sánh StrongApplicative
với nó, thì càng nhiều càng tốt.
** Bằng "bội số" Tôi đang đề cập một cách lỏng lẻo đến "có bao nhiêu" phần tử tùy ý của loại tham số của functor xảy ra trong một cư dân của loại tên miền của functor. Điều này không liên quan đến loại cụ thể mà functor được áp dụng, và do đó không liên quan đến bất kỳ cư dân cụ thể nào của loại tham số.
Không chính xác về điều này đã gây ra một số nhầm lẫn trong các bình luận, vì vậy đây là một số ví dụ về những gì tôi sẽ xem xét kích thước / bội số của các functor khác nhau là:
VoidF
: đã sửa, 0Identity
: đã sửa, 1Maybe
: biến, tối thiểu 0, tối đa 1[]
: biến, tối thiểu 0, vô hạn tối đaNonEmptyList
: biến, tối thiểu 1, vô hạn tối đaStream
: cố định, vô hạnMonoid m => (,) m
: đã sửa, 1data Pair a = Pair a a
: cố định, 2Either x
: biến, tối thiểu 0, tối đa 1data Strange a = L a | R a
: đã sửa, 1
(->) r
và chúng là đẳng cấu theo đúng cách đó.
(->) r
; bạn cần các thành phần của đẳng cấu để bảo toàn cấu trúc ứng dụng mạnh mẽ. Vì một lý do nào đó, kiểu chữ Representable
trong Haskell có một tabulate . return = return
định luật bí ẩn (điều này thậm chí không thực sự có ý nghĩa đối với những người cai ngục không đơn điệu), nhưng nó mang lại cho chúng ta 1/4 điều kiện chúng ta cần nói tabulate
và zip
là những hình thái của một loại đơn sắc phù hợp . 3 cái còn lại là luật bổ sung mà bạn phải yêu cầu.
tabulate
và index
là những hình thái của một thể loại phù hợp ..."
return
không phải là một vấn đề nghiêm trọng. cotraverse getConst . Const
là một triển khai mặc định cho return
/ pure
về mặt Distributive
, và, vì các phân phối / đại diện có hình dạng cố định, nên việc triển khai đó là duy nhất.