2017-12-31 36 views
-1

私はここに詰め込まれています。data型のインスタンスをMonadにすることはできますが、私はnewtypeでどうすればいいのか分かりません。newtypeをモナドのインスタンスにする方法

newtype Val a = Val {getVal :: [a]} deriving (Show) 

instance Monad Val where 
    return = Val 
    (>>=) (Val {getVal = l}) f = map f l 

私はこのように記述する場合:

instance Monad Val where 
return = Val 
(>>=) (Val {getVal = l}) f = {getVal = map f l} 

その後、私はエラーを取得し、それはparse error on input { 言います。

newtypeをMonadのインスタンスにするにはどうすればよいですか?

+3

'return'の定義はタイプチェックではありませんか? – Carl

答えて

1

GeneralizedNewtypeDerivingは、ここで最も便利なオプションです。

もっと長い選択肢は、安全な変換を使用して、Haskellにすべてのラップ/アンラッピングを自動的に実行するように依頼することです。残念なことに、これはいくつかの手作業の型の注釈と引数を必要とします。それ以外の型は、自動化された型変換が機能するにはあまりにも一般的です。

非常に便利ではありませんが、そこにあります。

{-# LANGUAGE InstanceSigs, ScopedTypeVariables, TypeApplications #-} 

import Data.Coerce 

newtype Val a = Val {getVal :: [a]} deriving (Show) 

instance Functor Val where 
    fmap :: forall a b. (a -> b) -> Val a -> Val b 
    fmap = coerce (fmap @ [] @ a @ b) 

instance Applicative Val where 
    pure :: forall a. a -> Val a 
    pure = coerce (pure @ [] @ a) 
    (<*>) :: forall a b. Val (a -> b) -> Val a -> Val b 
    (<*>) = coerce ((<*>) @ [] @ a @ b) 

instance Monad Val where 
    return :: forall a. a -> Val a 
    return = coerce (return @ [] @ a) 
    (>>=) :: forall a b. Val a -> (a -> Val b) -> Val b 
    (>>=) = coerce ((>>=) @ [] @ a @ b) 

別のオプションは、手動で上記のコードでは、自動化されcoerce、アンラップ/ラッピングを行うことであろう。しかし、これはかなり退屈で面倒です。それでも、型の注釈や引数は必要ありません。少なくとも、それはそれらに労力を節約します。

+0

これは私が学んでいるよりも進歩したことですが、私は新しい何かを学んだことがうれしいです:) –

3

>>=の定義にValのインスタンスを作成するときは、コンストラクタを提供する必要があります。

(>>=) (Val {getVal = l}) f = Val {getVal = map f l} 

Haskellは、道を「自由」の記録を持っていない、と言う、PureScriptはありません。 Haskellでは、{ x = y, v = u }はレコードを構築するための有効な構文ではありません。あなたは常にコンストラクタを提供する必要があります。 C { x = y, v = u }

+2

これは構文エラーを修正しますが、 'return'と'(>> =) 'の両方に型エラーがあります。 – 4castle

+0

確かに。私は質問に記載されているものに答えるだけで、さらに見栄えはしませんでした。私はまだこの答えを完全に残しておきます。 –

5

ここにいくつかの問題があります。まず、中括弧の前にレコード名を指定する必要があります。だからあなたは望みます

(>>=) (Val {getVal = l}) f = Val {getVal = map f l} 

今、いずれの機能もタイプチェックを行っていません。しかし、あなたが既に存在するリストモナドインスタンスに委任しているだけなので、修正はそれほど難しくありません。 returnは近いですが、結果をリストにラップする必要があります。

return x = Val [x] 

同様に、mapはレコード内には入れません。あなたは(>>=)のリストのモナドバージョンが欲しいです。

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= f} 

残念ながら、これはまだないかなりです。TypeCheck、f以来Valない[]を返すように設計されています。モナド操作の中でその修正を行う必要があります。

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= getVal . f} 

その後、あなたはおそらくApplicativeMonadのスーパークラスであることについて、エラーを取得します。これは、いくつかのヘルパー関数を簡単に適用することで解決できます。

import Control.Monad 

-- ... 

instance Functor Val where 
    fmap = liftM 

instance Applicative Val where 
    pure = return 
    (<*>) = ap 

すべてコンパイルする必要があります。サイドノートとして


あなたはGHCを使用している場合、自動的にnewtypeデータ型に対してあなたのインスタンスを与えるGeneralizedNewtypeDerivingと呼ばれる便利な機能があります。あなたはnewtype、ないdataにそう

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

newtype Val a = Val {getVal :: [a]} 
    deriving (Show, Functor, Applicative, Monad) 

これだけ作品のようにそれを使用することができ、それが唯一のGHCで動作しますので、あなたのコードを意図している場合、コンパイラ間で移植できるようにそれを使用しないでください。

関連する問題