簡體   English   中英

不同類型到不同實例構造函數的廣義轉換

[英]Generalized conversion of different types to different instance constructors

我正在嘗試定義類型 class 實例以使用Num的 1-4 個分量的向量,表示為元組。 這是我找到的最好的解決方案(來自我的最后一個問題

{-# LANGUAGE FlexibleInstances #-}

data OneTuple a = OneTuple a deriving Show

class MyClass a where
    prints :: a -> String

instance (Show n, Num n) => MyClass (n, n) where
    prints = show

instance (Show n, Num n) => MyClass (n, n, n) where
    prints (a, b, c) = foldl (++) "" $ map (++" ") $ map show [a,b,c]

instance (Show n, Num n) => MyClass (OneTuple n) where
    prints (OneTuple a) = show a

但這伴隨着prints 1被寫為prints $ OneTuple 1的要求,這是不可取的。 我嘗試為此實現一個tuplify function,但是

wrap :: (MyClass a) => (String -> String) -> a -> String
wrap f = f . prints

tuplify :: (MyClass b) => a -> b
tuplify (a,b) = (a,b)
tuplify (a,b,c) = (a,b,c)
tuplify a = OneTuple a

stringFunction :: String -> String
stringFunction = id

vectorToString x = wrap stringFunction $ tuplify x

不編譯。 我試圖達到這樣的東西

vectorToString (1,2,3)
vectorToString (1,2)
vectorToString 1

這甚至可能嗎? 我想我記得讀過一些關於函數的行為不應該如何根據輸入類型而改變的東西,這正是tuplify正在做的事情。 有沒有其他方法可以解決這個問題? 這是來自 ghci 的錯誤消息:

question.hs:18:9: error:
    • Couldn't match expected type ‘a’ with actual type ‘(a0, b0)’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          tuplify :: forall b a. MyClass b => a -> b
        at question.hs:17:1-32
    • In the pattern: (a, b)
      In an equation for ‘tuplify’: tuplify (a, b) = (a, b)
    • Relevant bindings include
        tuplify :: a -> b (bound at question.hs:18:1)
   |
18 | tuplify (a,b) = (a,b)
   |         ^^^^^

question.hs:18:17: error:
    • Couldn't match expected type ‘b’ with actual type ‘(a0, b0)’
      ‘b’ is a rigid type variable bound by
        the type signature for:
          tuplify :: forall b a. MyClass b => a -> b
        at question.hs:17:1-32
    • In the expression: (a, b)
      In an equation for ‘tuplify’: tuplify (a, b) = (a, b)
    • Relevant bindings include
        b :: b0 (bound at question.hs:18:12)
        a :: a0 (bound at question.hs:18:10)
        tuplify :: a -> b (bound at question.hs:18:1)
   |
18 | tuplify (a,b) = (a,b)
   |                 ^^^^^

我想我記得讀過一些關於函數的行為不應該根據輸入類型而改變的內容

聽起來您正在考慮Liskov 替換原則 但這是來自 OO 世界的東西,不適用於 Haskell。 您甚至無法真正制定它,因為 Haskell 沒有子類型。

Haskell 確實有一個相關的原則:參數多態函數不能有不同的行為,具體取決於使用的類型。 但這與其說是程序員應該遵守的原則,不如說是由語言強制執行的 這就是為什么您的tuplify不同案例無法編譯的原因之一:您嘗試在 types 上進行模式匹配 這不是您可以在 Haskell function 聲明中執行的操作。

然而,這幾乎正是類型類所做的,事實上你自己的類型類已經做到了。

您的tuplify存在另一個問題:簽名MyClass b => a -> b意味着調用者可以選擇ab是什么,然后 function 必須能夠以某種方式在它們之間進行轉換。 例如,它必須能夠將 2 元組轉換為 3 元組。 你可能想要表達的是,每個輸入類型都與一個結果類型相關聯——即一個類型級別的 function。 我們稱這些類型為族,實際上你可以寫

{-# LANGUAGE TypeFamilies #-}

type family Tuplified t where
  Tuplified (a,b) = (a,b)
  Tuplified (a,b,c) = (a,b,c)
  Tuplified t = t

但是,這仍然沒有真正給您帶來任何進一步的好處:現在您有了一個類型級別的 function,但沒有針對個別情況的相應值級別函數。 那 function 必須有類型

tuplify :: t -> Tuplified t

但是如果不使用 class 類型,就無法定義它。 可以編寫這樣的類型 class,但隨后您將遇到與您在其他問題中首先嘗試解決的問題相同的問題!

我不明白這有什么意義。 可能,您的 class 實例實際上應該更像標准的Show class:

instance MyClass Integer where prints = show
instance MyClass Int where prints = show

instance (MyClass a, b~a) => MyClass (a,b) where
  prints (x,y) = '(' : prints x ++ "," ++ prints y ++ ")"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM