質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

GHC

Glasgow Haskell Compiler(GHC) は Haskell コンパイラです 異なる複数のアーキテクチャのネイティブコードや、C言語へコンパイルする事ができます。

Q&A

解決済

3回答

3112閲覧

`^' の Warning

zeroyonichikyu

総合スコア37

Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

GHC

Glasgow Haskell Compiler(GHC) は Haskell コンパイラです 異なる複数のアーキテクチャのネイティブコードや、C言語へコンパイルする事ができます。

0グッド

0クリップ

投稿2015/05/27 03:03

Haskell 学習中に理解できない Warning に突き当たったので、どなたかご教授いただければと思います。
最小構成とWarning の内容を以下に記載します。

lang

1{-# OPTIONS -Wall -Werror #-} 2 3square' :: Double -> Double 4square' x = x ^ 2

lang

1test.hs:5:15: Warning: 2 Defaulting the following constraint(s) to type `Integer' 3 (Integral b0) arising from a use of `^' at test.hs:5:15 4 (Num b0) arising from the literal `2' at test.hs:5:17 5 In the expression: x ^ 2 6 In an equation for square': square' x = x ^ 2

環境は以下の通りです。

lang

1$ ghc --version 2The Glorious Glasgow Haskell Compilation System, version 7.6.3

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

処理系がデフォルトの型を使ったという警告です

どうして型が決定できないか

読みやすさのために、質問者のコードを再掲します。

haskell

1{-# OPTIONS -Wall -Werror #-} 2 3square' :: Double -> Double 4square' x = x ^ 2

このエラーは数値リテラル2の型が決定できないために、生じています。
Haskellでは数値リテラルの型は多相性を持ちます。
対話環境で、つぎのように試してみてください。

haskell

1> 2 :: Integer 22 3> :type it 4it :: Integer 5> 2 :: Double 62.0 7> :type it 8it :: Double 9> :type 2 102 :: Num t => t

数値リテラル2は多相的であり、実際に計算が行われるときには、Numクラスに含まれる、どれかの型に決定されます。

さて、演算子(^)の型を見てみましょう。

haskell

1(^) :: (Num a, Integral b) => a -> b -> a

第1引数はNumクラスに含まれる型の値、第2引数はIntegralクラスに含まれる型の値、返り値は第1引数と同じ型の値、となります。
ここで、問題の式を見てみましょう。

haskell

1x ^ 2

リテラル2はNumクラスである型の値となります。また、演算子(^)を適用していることから、よりきびしく、Integralクラスの型の値であることを要求されます。しかし、それだけです。これだけでは数値2の型が定まりません。

型の決まらない「整数値」のデフォルトはInteger

ここで、Haskellには、型の決まらない「整数値」をデフォルトでIntegerとする、というルールがあります。そのルールで数値2の型はIntegerとなります。
しかし、この「デフォルトの型」はコードを書いた人の意図とは異なるかもしれません。そこで警告が出ます。

解決の方法の、いくつかを挙げます。
まず、質問者が -Wall -Werror としているのは大変良い習慣かと思います。
そこで、それを生かす方向で、いくつかの解決策を挙げておきます。

デフォルトの型を使うことへの警告をつぶす

コードを書く人が、Haskellがデフォルトとして選ぶ型を信頼しているなら、つぎのようにします。

haskell

1{-# OPTIONS -Wall -fno-warn-type-defaults -Werror #-}

こうすると、Haskellがデフォルトの型を選択したときに、警告しなくなります。

型注釈をつける

より、おすすめの方法は、型注釈をつけることです。

haskell

1square' x = x ^ (2 :: Integer)

このようにすれば、明示的に数値2の型をIntegerと特定することができます。
Int型の値のほうが、すこしだけ、空間効率が良い気がするので、つぎのようにするのも、いいかもしれません。

haskell

1square' x = x ^ (2 :: Int)

演算子(**), (^), (^^)について

それぞれの型を再掲します。

haskell

1(**) :: Floating a => a -> a -> a 2(^) :: (Num a, Integral b) => a -> b -> a 3(^^) :: (Fractional a, Integral b) => a -> b -> a

実は、型だけからでも、あるていど、これらのちがいは予想できます。
演算子(**)では、冪乗する数である第2引数はFloatingクラスの型の値です。
つまり、浮動小数点っぽい値だということです。
整数でなくてもいい、つまり、つぎのようなことができます。

haskell

1> 3 ** 0.5 21.7320508075688772

それに対して、演算子(^)では、第2引数は整数値です。
つまり、x ^ yは、「xをy回、かけあわせたもの」です。
このとき、xはNumクラスのインスタンスである型の値なら、何でもいいです。
Numクラスのクラス関数に「かけ算」があるためです。

最後の(^^)については、第1引数がFractionalクラスの型、つまり、分数(小数)となっています。整数では、負の数での冪乗ができないためです。

haskell

1> 2 ^ (- 1) 2Exception: Negative exponent 3> 2 ^^ (- 1) 40.5

どうして、(**)で解決したか

演算子(**)の型は、つぎのようになっています。

haskell

1(**) :: Floating a => a -> a -> a

質問者が新しく定義した関数は、つぎのようになります。

haskell

1square' :: Double -> Double 2square' x = x ** 2

演算子(**)は、第1引数、第2引数、返り値のすべてが同じ型であることを、要求しています。
そしてsquare'では引数と返り値とがDouble型の値であることを、要求します。
これらから、数値2の型がDouble型に決定されます。

ただ、この解決は、あまり好ましくないと感じます。
「ある数を、1.5乗でもなく2.5乗でもなく2乗したい」という意図であれば、
上記の解決策がぴったりですが、
「ある数を、2回かけあわせたい」という意図であれば、演算子(^)が、ぴったりかと思います。
そして、第2引数の数値リテラルには、好みの整数型を指定してあげるのが良いと思われます。

投稿2017/05/25 04:28

編集2017/05/26 01:00
YoshikuniJujo

総合スコア33

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zeroyonichikyu

2017/05/30 03:03

丁寧かつ詳細な解説ありがとうございます. 質問したときは数値リテラルの型についてよくわかっておらず, 混乱していました. teratailのUIに慣れておらず, 今方法を調べていますが, 分かり次第解決した方法をこちらの回答に付け替えさせていただきます.
YoshikuniJujo

2017/05/30 08:00

こちらこそ、良い質問を、ありがとうございました。
guest

0

自己解決

@takotakot さんの回答をヒントに warning の理由について調べ、

lang

1*Main> :t (^) 2(^) :: (Integral b, Num a) => a -> b -> a 3*Main> :t (**) 4(**) :: Floating a => a -> a -> a 5*Main> :t (^^) 6(^^) :: (Fractional a, Integral b) => a -> b -> a 7*Main> :t 2 82 :: Num a => a

と、ここでようやくエラーメッセージの意味が理解出来ました。
(エラーではないのは定義上は不一致だが、計算上は問題ないと推論されたからでしょうか)

ご提案の通り ** を使用するか、

lang

1square' :: Double -> Double 2square' x = x ^ (2 :: Int)

とすれば warning がでないことを確認しました。

累積の演算子が3種類ある理由や使い分けがまだイメージできませんが、今後の課題にしたいと思います。

投稿2015/05/27 04:56

zeroyonichikyu

総合スコア37

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

英語で何と書いてあるか、分かりますか?キーワードは Integer。エラーメッセージはきちんと読みましょう。「Haskell 累乗」で検索するだけで見つかりますよ?
Haskell基礎文法最速マスター を読めば、どの演算子を用いればよいか分かります。** を用いるべきですね。

投稿2015/05/27 03:12

takotakot

総合スコア1111

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zeroyonichikyu

2015/05/27 04:29

Wallオプションを外すとコンパイルが通り、結果も問題なかったので、演算子そのものが間違いとは思いつきませんでした。 Integer と ^ と ** については調べてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問