2

基本的にバイナリツリーであるデータ型を構築しようとしています。各ノードの左側のブランチは、各ノードの右側のブランチの変数に作用できる関数です。私はHaskellを初めて使用し、これが正しい方法で行われるかどうかはわかりませんが、現在の問題は、自分の型をShow型クラスに追加する方法がわからないことです。これが私の試みです:

{-# LANGUAGE ExistentialQuantification #-}
-- file: TS.hs                                                                                                                                                       

data TypeSentence a = forall b. Apply (TypeSentence (b->a)) (TypeSentence b)
                    | Expr a

instance (Show a) => (Show (TypeSentence a)) where
        show (Expr x) = show x
        show (Apply x y) = (show x) ++ " " ++ (show y)

instance (Show (TypeSentence b->a)) where
    show (Expr x) = show "hello"

x = Expr 1
f = Expr (+1)
s = Apply f x

ただし、これをghciにロードすると、次のエラーが発生します。

TS.hs:9:24:                                                                                                                                                          
     Could not deduce (Show (b -> a)) from the context ()                                                                                                             
     arising from a use of `show' at TS.hs:9:24-29                                                                                                                  
      Possible fix:                                                                                                                                                    
      add (Show (b -> a)) to the context of the constructor `Apply'                                                                                                  
      or add an instance declaration for (Show (b -> a))                                                                                                             
        In the first argument of `(++)', namely `(show x)'                                                                                                               
        In the expression: (show x) ++ " " ++ (show y)                                                                                                                   
        In the definition of `show':                                                                                                                                     
           show (Apply x y) = (show x) ++ " " ++ (show y)                                                                                                               
 Failed, modules loaded: none. 

Show(b-> a)宣言を追加する方法について何かアイデアはありますか?

ありがとう。

4

4 に答える 4

7

記述されたコードにはいくつかの問題があるので、それらを1つずつ見ていきます。

  1. の特に有益なインスタンスを追加することはできませんShow (a -> b)。あなたがそれをどのように書かなければならないかを考えてください:

    instance Show (a -> b) where
      show f = error "What goes here?"
    

    は関数なのでf、値に適用する以外にできることはありません。は完全ポリモーフィック型であるため、適用するa型の値を作成することはできません。だからあなたの唯一の選択肢は次のようなものですaf

    instance Show (a -> b) where
      show _ = "<function>"
    

    Daniel Fischerがコメントで述べたように、これはText.Show.Functionsモジュールで利用できます。しかし、私は実際にはこれを気にしません。私はただのようなものを書きます

    instance Show a => Show (TypeSentence a) where
      show (Apply _ x) = "Apply _ " ++ show x -- This still won't work; see below
      show (Expr x)    = "Expr " ++ show x
    

    show関数に対して返すことができる文字列は1つだけなので、それを直接インライン化するだけです。

  2. それでも、そのインスタンスを作成することできません。Show上記のインスタンスをコンパイルしようとすると、次のエラーが発生します。

    TS.hs:8:36:
        Could not deduce (Show b) arising from a use of `show'
        from the context (Show a)
          bound by the instance declaration
          at TS.hs:7:10-40
        Possible fix:
          add (Show b) to the context of
            the data constructor `Apply'
            or the instance declaration
        In the second argument of `(++)', namely `show x'
        In the expression: "Apply _ " ++ show x
        In an equation for `show': show (Apply _ x) = "Apply _ " ++ show x
    

    問題は、の定義でTypeSentence、任意の存在的に隠された型によってパラメータ化された変数(の定義のようにApplyバインドされた)を非表示にすることです。ただし、表示可能であるという保証はないため、上記で生成されたエラーであるチェックを入力しないでください。任意であるため、のインスタンスはありません。したがって、それを取り除くための最も簡単なアプローチは次のようになります。xshowTypeSentencebbshow xShow bb

    instance Show a => Show (TypeSentence a) where
      show (Apply _ _) = "Apply _ _"
      show (Expr x)    = "Expr " ++ show x
    

    そして、それは特に有用ではありません。Showしたがって、の良いインスタンスはないかもしれませんTypeSentenceShow(そしてそれは問題ありません。多くの便利なタイプにはインスタンスがありません。)

  3. これは他のすべてとは無関係です。宣言は、関数fromから;のinstance Show (TypeSentence b -> a)インスタンスを宣言しようとします。それをとして再括弧で囲んだ場合でも、コンパイルするにはと拡張子の両方が必要です。おそらくあなたはただ斧するべきです。ShowTypeSentence bainstance Show (TypeSentence (b -> a))FlexibleInstancesOverlappingInstances

于 2012-06-28T20:59:33.780 に答える
4

さて、これを推論しましょう。提案されたShowインスタンスのshowメソッドは、いくつかの関数で呼び出されますf :: b -> a

instance Show (b -> a) where
    show f = ...

あなたの方法は何ができますshowか?まあ、それはいくつかを生成する必要がありStringますが、それはどのようにそれを行いますか?

さて、のタイプはfですのでb -> a、あなたができる唯一のことfはそれをタイプの何かに適用することですb。しかしshow、型の引数はなくbShowクラスには型の定数がないbため、このshowメソッドで実行できるのは、fにそれを適用することだけですundefined。厳密であるかどうかに応じて、エラーが発生する場合と発生しない場合があります。これは制御できません。いずれにせよ、一部の引数でエラーが発生しfたくないと確信しています。show

しかし、いずれにせよ、から結果を取得したとしてもf undefined、この結果には型があり、使用可能な型の関数がないため、とにかくa定義でできることは何もありません。(もし持っていたとしても、持っていなければ、同じ位置にいるでしょう。)aa -> whateverwhateverString

したがって、で実行できる意味のあることは何もありません。他に引数fがないため、メソッドで実行できるのは、他の引数に依存しない値を返すことだけですf。したがって、メソッドの戻り値は定数、またはである必要がありundefinedます。使用undefinedするのはばかげているので、このメソッドが実行できる唯一の賢明なことshowは定数を返すことですString

instance Show (b -> a) where
    show _ = "<function>"

Daniel Fischerがあなたの質問に対するコメントで述べているように、これはすでにで利用可能ですText.Show.Functions

しかし、ここでの教訓は、これをあなたの質問を通して推論する方法の例として取り上げることです。これはHaskellの優れた点の1つです。関数を確認するだけで、関数が実行できること、実行できないこと、実行しなければならないことを証明できることがよくあります。たとえば、を持っている場合、十分に愚かではないfoo :: (a -> b) -> [a] -> [b]と仮定すると、無償で使用されない場合、結果のsは、引数の要素に型引数を適用することによって取得されると推測できます。タイプの値を生成する他の方法はありません。(まだ推測していなかった場合、そのタイプの最も自然な関数はです。)fooundefinedb[b]a -> b[a]foobmap :: (a -> b) -> [a] -> [b]

于 2012-06-28T20:43:37.450 に答える
1

@Davorakのコメントはあなたが望むものだと思います。

https://stackoverflow.com/a/15846061/6289448

ここで共有します。でテストに合格しghc 8.6.5ます。

Data.Typeableを使用するすべての関数の固定文字列を超える部分的な解決策があります。

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Typeable

instance (Typeable a, Typeable b) => Show (a->b) where
  show _ = show $ typeOf (undefined :: a -> b)

ghciで

> let test :: Int->Int; test x = x + x
> test
Int -> Int

残念ながら、型シグネチャがないと、型はデフォルトになります。

> let test x = x + x
> test
Integer -> Integer

a-> b-> cはa->(b-> c)と同じであるため、このソリューションは複数の関数アリティで機能します。これは、d =b->cの場合はa->dと書くこともできます。

> let m10 a b c d e f g h i j = a * b * c * d * e * f * g * h* i * j
> m10
Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer
        -> Integer -> Integer -> Integer -> Integer

ただし、関数のパラメーターに入力可能なクラスがあるかどうかが不明な場合、このメソッドは機能しません。ただし、map(+1)は機能しますが、mapは機能しません。

> map (+1)
[Integer] -> [Integer]
> map

<interactive>:233:1:
...

Data.Dataの内部と1つか2つの実験を一瞥した後、もう少し一般化されてより多くの機能をカバーするようにリファクタリングできるようです。



上記の実装が気に入らない場合は、自分で実装してください。(もっと良い方法があれば教えてください!)

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}

newtype GenType a =
    GenType
        { asTypeStr :: String
        }

class GenTypeArbitrary a where
    gtArbitrary :: a -> GenType a

instance GenTypeArbitrary String where
    gtArbitrary :: String -> GenType String
    gtArbitrary _ = GenType "String123"

instance GenTypeArbitrary Bool where
    gtArbitrary :: Bool -> GenType Bool
    gtArbitrary _ = GenType "Bool123"

instance GenTypeArbitrary Int where
    gtArbitrary :: Int -> GenType Int
    gtArbitrary _ = GenType "Int123"

instance (GenTypeArbitrary a, GenTypeArbitrary b) => GenTypeArbitrary (a -> b) where
    gtArbitrary :: (GenTypeArbitrary a, GenTypeArbitrary b) => (a -> b) -> GenType (a -> b)
    gtArbitrary _ = GenType $ aTypeStr' ++ " --> " ++ bTypeStr
      where
        aTypeStr = asTypeStr (gtArbitrary (undefined :: a))
        aTypeStr' =
            if "-->" `isInfixOf` aTypeStr
                then "(" ++ aTypeStr ++ ")"
                else aTypeStr
        bTypeStr = asTypeStr (gtArbitrary (undefined :: b))

instance  (GenTypeArbitrary a, GenTypeArbitrary b) => Show (a -> b) where
  show f = asTypeStr $ gtArbitrary f

test1 :: Int -> String
test1 x = ""

test2 :: Int -> String -> Int -> Bool -> Bool
test2 _ _ _ _ = False

test3 :: Int -> ((String -> Int) -> Bool) -> Bool
test3 _ _ = False

test4 :: Int -> (Bool -> (String -> Int)) -> Bool
test4 _ _ = False


λ > show  (test4)
    "Int123 --> (Bool123 --> String123 --> Int123) --> Bool123"
it :: String


...


λ > show  (test3)
    "Int123 --> ((String123 --> Int123) --> Bool123) --> Bool123"
it :: String


于 2019-07-29T01:54:08.543 に答える
0

関数の定義域が有限集合である場合は、すべての点で関数の値を出力できます。Haskellでは、型クラスを使用して、次のような関数を使用してこれを行うことができIxますBounded

rangeF :: (Ix a, Bounded a) => [a]
rangeF = range (minBound, maxBound)
于 2012-06-28T22:00:53.643 に答える