2015-08-19 32 views
12

Tôi đã viết một Newtype Const3 đó là rất giống với Const, nhưng chứa đầu tiên trong ba đối số loại nhất định:Tôi có thể triển khai loại mới này làm thành phần của các loại khác không?

newtype Const3 a b c = Const3 { getConst3 :: a } 

tôi có thể xác định rất nhiều trường hợp có ích cho Newtype này, nhưng tôi sẽ phải làm nó tất cả bản thân mình.

Tuy nhiên, chức năng Tôi đang áp dụng theo mức độ loại tương tự như chức năng

\a b c -> a 

@pl nói với tôi là tương đương với const . const.

Cả hai (.)const khớp với trình bao bọc newtype: ComposeConst. Vì vậy, tôi figured tôi muốn có thể viết:

type Const3 = Compose Const Const 

Và kế thừa trường hữu ích tự động, chẳng hạn như:

instance Functor (Const m) 
instance (Functor f, Functor g) => Functor (Compose f g) 
-- a free Functor instance for Const3! 

Nhưng GHC không đồng ý:

const3.hs:5:23: 
    Expecting one more argument to ‘Const’ 
    The first argument of ‘Compose’ should have kind ‘* -> *’, 
     but ‘Const’ has kind ‘* -> * -> *’ 
    In the type ‘Compose Const Const’ 
    In the type declaration for ‘Const3’ 

này có vẻ là có liên quan đến các loại ComposeConst:

*Main> :k Compose 
Compose :: (* -> *) -> (* -> *) -> * -> * 
*Main> :k Const 
Const :: * -> * -> * 

Vì vậy, sau một chút tìm kiếm, tôi phát hiện ra rằng có một phần mở rộng GHC gọi PolyKinds cho phép tôi làm điều gì đó như:

{-# LANGUAGE PolyKinds #-} 
newtype Compose f g a = Compose { getCompose :: f (g a) } 
newtype Const a b = Const { getConst :: a } 

Và như một phép màu các loại là đúng:

*Main> :k Compose 
Compose :: (k -> *) -> (k1 -> k) -> k1 -> * 
*Main> :k Const 
Const :: * -> k -> * 

Nhưng tôi vẫn không thể soạn chúng để viết Const3 = Compose Const Const.

const3.hs:12:23: 
    Expecting one more argument to ‘Const’ 
    The first argument of ‘Compose’ should have kind ‘* -> *’, 
     but ‘Const’ has kind ‘* -> k0 -> *’ 
    In the type ‘Compose Const Const’ 
    In the type declaration for ‘Const3’ 

Điều gì mang lại? Có cách nào thông minh để làm điều này, vì vậy tôi có thể gặt hái những lợi ích của việc kế thừa các trường hợp Functor vv từ ConstCompose?

(Là một lưu ý phụ, ý nghĩ ban đầu mà dẫn tôi đến Const3 đang viết:

newtype Const3 a b c = Const3 { getConst3 :: a } 

instance Monoid m => Category (Const3 m) where 
    id = Const3 mempty 
    Const3 x . Const3 y = Const3 (mappend x y) 

bắt ý tưởng rằng một monoid là một phạm trù đơn đối tượng Nó sẽ được tốt đẹp nếu. có một giải pháp mà vẫn cho phép tôi viết ví dụ trên bằng cách nào đó.)

+4

'loại Const3 a b c = Soạn (một Const) (b Const) c'? Vì 'nhưng 'Const' có thông báo loại‘ * -> * -> * ’' ngụ ý, bạn cần cung cấp một kiểu cho hàm tạo kiểu để đến kiểu được mong đợi bằng 'Soạn'. – danidiaz

Trả lời

2

Điều khó hiểu - hoặc, ít nhất, điều gây nhầm lẫn tôi —có phải * hoạt động như loại bê tông , không phải loại biến.Vì vậy mà không PolyKinds, Compose có một loại đó là giống như:

compose :: (A -> A) -> (A -> A) -> A -> A 

Điều quan trọng, chúng ta không thể thay thế một A với A -> A vì họ muốn được các loại khác nhau, vì vậy, bởi cùng một logic, chúng ta không thể thay thế * với * -> * một trong hai.

Ngay cả với PolyKinds, các loại vẫn không đúng. Cụ thể, Compose hy vọng (k -> *) là đối số đầu tiên của nó và bạn đang cố gắng cung cấp cho nó (k -> (k2 -> *)).

Lý do bạn buộc phải trả lại loại * là vì bạn đang sử dụng newtypes và loại mới phải trả về loại bê tông (ví dụ: loại *). Tôi cố gắng để khắc phục điều này bằng cách chuyển Compose thành một từ đồng nghĩa kiểu mà cuối cùng đã hoàn toàn loại chúng ta muốn (với PolyKinds):

type Compose f g a = (f (g a)) 

λ> :k Compose 
Compose :: (k1 -> k) -> (k2 -> k1) -> k2 -> k 

Tuy nhiên, sử dụng này vẫn đã cho tôi một lỗi tương tự, và tôi không chắc chắn nếu chúng ta có thể làm cho nó hoạt động đúng. Vấn đề nảy sinh vì áp dụng Compose đến Const đầu tiên cho chúng ta một loại với một * trong nó, có lẽ vì trên hạn chế của loại bí danh như thế này:

λ> :k Compose Const 
Compose Const :: (k -> *) -> k -> k1 -> * 
+0

Tôi nghĩ rằng đó là 'Const', đó là vấn đề khó nhất ở đây, không phải là' Soạn'. Cụ thể, trong phần mở rộng mong muốn 'Const (Const a) bc',' Const' đầu tiên cần phải có loại '(* -> *) -> * -> * -> *' và thứ hai cần phải có loại '* -> * -> * ', số lượng đối số khác nhau, vì vậy chúng không thể là một' newtype' chung. –

+0

@ ØrjanJohansen: Ồ, tốt. Nhưng làm cho 'Const' một từ đồng nghĩa kiểu không thực sự làm những gì chúng ta muốn ... –

+2

Điều thú vị là, nếu bạn tạo ra cả hai' Const' và 'Compose' đa dạng, bạn có thể nhận được': type' của GHCi để hiển thị chính xác loại, ví dụ 'Soạn Const Const Int Bool String :: *', nhưng nó vẫn không hoạt động như một chú thích kiểu: "Gõ từ đồng nghĩa' Const' phải có 4 đối số, nhưng đã được đưa ra 2 trong biểu thức: '42 :: Soạn thư Const Chuỗi Const Int Bool' " – Cactus

2

Từ câu trả lời khác, nó có vẻ như nó không phải là dễ dàng, tuy nhiên nếu điều duy nhất bạn muốn có là những trường hợp "tự do", một cách nhanh chóng là sử dụng một newtype so với bình thường Const với GeneralizedNewtypeDeriving mở rộng:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
{-# LANGUAGE DeriveTraversable #-} 
{-# LANGUAGE PatternSynonyms #-} 
module ConstThree (Const3,pattern Const3,getConst3) where 
import Data.Foldable 
import Data.Traversable 
import Control.Applicative 
import Data.Monoid 

newtype Const3 a b c = MkConst3 (Const a c) deriving (Functor,Applicative,Foldable,Traversable,Eq,Ord,Show,Monoid) 

pattern Const3 :: a -> Const3 a b c 
pattern Const3 x = MkConst3 (Const x) 

getConst3 :: Const3 a b c -> a 
getConst3 (Const3 x) = x 

Ở phía trên, tôi cũng đang sử dụng PatternSynonyms để ẩn việc sử dụng nội bộ của Const từ khách hàng.

Đây là những gì bạn nhận được:

λ> :t Const3 
Const3 :: a -> Const3 a b c 
λ> :t getConst3 
getConst3 :: Const3 a b c -> a 
λ> :i Const3 
pattern Const3 :: a -> Const3 a b c 
     -- Defined at /tmp/alpha-dbcdf.hs:13:5 

type role Const3 representational phantom phantom 
newtype Const3 a b c = MkConst3 (Const a c) 
     -- Defined at /tmp/alpha-dbcdf.hs:10:5 
instance Eq a => Eq (Const3 a b c) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:100 
instance Functor (Const3 a b) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:59 
instance Ord a => Ord (Const3 a b c) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:103 
instance Show a => Show (Const3 a b c) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:107 
instance Monoid a => Applicative (Const3 a b) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:67 
instance Foldable (Const3 a b) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:79 
instance Traversable (Const3 a b) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:88 
instance Monoid a => Monoid (Const3 a b c) 
    -- Defined at /tmp/alpha-dbcdf.hs:10:112 

Và như mong đợi, bạn có thể làm:

instance Monoid m => Category (Const3 m) where 
    id = Const3 mempty 
    Const3 x . Const3 y = Const3 (mappend x y) 
Các vấn đề liên quan