1

Haskell が次のコードの 8 行目の型を判別できない理由がわかりません。expressMaybe 関数の型シグネチャは、結果の型が 2 つの入力パラメーターの型と同じであることを確立していませんか?

{-# LANGUAGE MultiParamTypeClasses #-}

class Gene g n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

私が得るエラーは次のとおりです。

Amy20.hs:8:40:
    Ambiguous type variable `n0' in the constraint:
      (Gene g n0) arising from a use of `express'
    Probable fix: add a type signature that fixes these type variable(s)
    In the first argument of `Just', namely `(express a b)'
    In the expression: Just (express a b)
    In an equation for `expressMaybe':
        expressMaybe (Just a) (Just b) = Just (express a b)
Failed, modules loaded: none.

RankNTypes と ScopedTypeVariables をいじってみましたが、エラーをなくす方法がわかりませんでした。

よろしくお願いします。

編集:問題を理解したので、Fundeps を使い慣れているので、Fundeps を使用しました。私のアプリケーションでは、遺伝子をエンコードするために複数の「アルファベット」を使用することはあまり意味がありません。ただし、これまで型ファミリを使用したことがないので、それについても調べます。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Gene g n | g -> n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing
4

2 に答える 2

12

このことを考慮:

instance Gene String Int where
  express _ _ = "INT"

instance Gene String Double where
  express _ _ = "DOUBLE"

expressMaybe (Just "") (Just "")

そのコードは生成する必要があります(Just "INT")(Just "DOUBLE")?はい、Haskellはの結果がexpressMaybe引数と同じ型になることを知っています。gただし、同じタイプに対して異なる。を持つ複数のインスタンスが存在する可能性があるため、ここで使用するインスタンスがわかっているわけではありませんn

nあなたの場合、型ごとに1つの型しかない場合は、g型族や関数従属性などの拡張機能を使用して、その事実を型システムに表現することを検討してください。

于 2012-10-09T10:32:26.293 に答える
1

機能的な依存関係を避け、型ファミリを使用することをお勧めします。それらはより楽しく、より直感的です。

{-# LANGUAGE TypeFamilies, KindSignatures #-}

class Gene g where
  type Nucleotide g :: *  -- each instance has an associated type
  express :: g -> g -> g
  encode :: [Nucleotide g] -> g -- probably doesn't make sense, but need an example
  -- there will be other functions that use the "Nucleotide g" type parameter

私たちが持っているとしましょう

data ACTG = A | C | T | G
data ACTGgene = ACTGgene [ACTG]

基本的に、型レベルでの単純なパターン マッチングによって型関数を定義します。

instance Gene ACTGgene where
  type Nucleotide ACTGgene = ACTG
  encode ns = ACTGgene ns
  express = error "I'm out of my depth here because I gave up Biology when I hit 14."

これで、コードがコンパイルされます。

expressMaybe :: Gene g => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- compiles fine
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

遺伝子型は基本的に、使用しているヌクレオチドによって決定されるか、異なるヌクレオチドを交換可能に使用できるかどうかはわかりませんが、問題の解決策の 1 つは、代わりにコンストラクター クラスを使用することです。

class Gene g where
   express :: g n -> g n -> g n
   encode :: n -> g n
   -- more stuff

これは、次のようなデータ宣言に適合します。

data ACTG = A | C | T | G
data ListGene n = ListGene [n]

instance Gene ListGene where
  ...

しかし、それは問題のドメインにとってパラメトリックすぎるかもしれません。わかりません。


確かに型族を試してみるべきです。やった。今、私は彼らを愛しています。- リンクされた回答には、さらに読むためのリンクが最後に含まれています。

于 2012-10-10T00:26:10.530 に答える