何故正規表現を使いたく無いかよく分かって無いですが,ワイドルカードや許容文字が複数ある場合などは,
regex-applicative-text や regex-tdfa などをよく使いますregex-applicative-text や regex-tdfa などをよく使いますTypeable のインスタンス解決が破綻してる件ってどうなってるんでしょうか? DeriveDataTypeable はお亡くなりになる感じなのか,それともderiving自体は現状でも書いておくべきなんでしょうか?Typeable でderivingされたものは無視するという方針でいく感じですかね? そうなると現状, AutoDeriveTypeable が有効になってる前提で書くのがいいのか, DeriveDataTypeable でいちよderivingを書いていくのがいいのかどっちなんですかね?instance Arbitrary ってライブラリ側に書くのとテスト側に orphan instance として書くの、どちらがいいんでしょう。{-# OPTIONS_GHC -fno-warn-orphans #-} を書いておきます。imperative-edsl の System.IO.Fake モジュールで定義されている fakeIO 関数を使ってこんな感じのテストを書いてみたんですが、他の方法って何かあります?
module Main where
import System.IO.Fake (fakeIO)
import Test.Hspec (hspec, describe, it)
import Test.Hspec.Expectations (shouldReturn)
main = hspec $
describe "Prelude.head" $
it "returns the first element of a list" $
fakeIO act "Haskell" `shouldReturn` "('H','a')\n"
act :: IO ()
act = do
x <- getChar
getChar
y <- getChar
print (x, y)
Failures:
Main.hs:10:
1) Prelude.head returns the first element of a list
expected: "('H','a')\n"
but got: "('H','s')\n"
IO () をテストする他の方法という意味ですー。IO している関数に手を入れることができるなら、 :point_down: こういうちょっとした依存性注入パターンを使いますね。
data Env m =
Env { print :: String -> m (), read :: m String }
useIo :: Monad m => Env m -> m ()
useIo e = do
s <- read e
print s
IO するかどうかを Env の中身に委ねつつ、 useIo を実質純粋な関数として維持できます。Env を ReaderT を経由して渡すようにすれば、見かけとしてもバッチリになるでしょう。silently パッケージというのもあります。stdout を再オープンして書き換える、という大胆不敵なやり方をとっています。
module Main where
import Data.Conduit (ConduitM, await, yield, runConduit, runConduitPure, (.|))
import qualified Data.Conduit.List as CL
import Conduit (stdinC, stdoutC)
import Data.ByteString.Char8 (pack, unpack)
import Test.Hspec (hspec, describe, it)
import Test.Hspec.Expectations (shouldBe)
main = hspec $
describe "Prelude.head" $
it "returns the first element of a list" $
runConduitPure (CL.sourceList "Haskell" .| act .| await) `shouldBe` Just ('H','a')
act :: Monad m => ConduitM Char (Char, Char) m ()
act = do
Just x <- await
await
Just y <- await
yield (x, y)
actIO :: IO ()
actIO = runConduit $
stdinC .|
CL.concatMap unpack .|
act .|
CL.map (pack . show) .|
stdoutC
MIN_VERSION_* で4桁を指定する方法ってないですよね?hoge-1.2.3.4 と hoge-1.2.3.5 で振る舞いを分ける方法って無いですよね?fakeIO の実装は silently にインスパイアされたものなので、仕組み自体は silently に近いです。Env を使った依存性注入パターンでは、例えば通常 putStrLn を使って出力している箇所を Env の print に差し替える必要がありそうですね。このやり方に近い感じですか?
https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/
Env を implicit に渡していると考えれば近いかと思います。Env は直接渡した方が、型クラスより柔軟でお勧めです。テスト可能な形で入出力を切り離す というよりは、既存のコードをできるだけ変更せずにテストするためにはどうしたら良いのかな?という感じでした。 (ちゃんと明示してなくてすみません。)Conduit あまり詳しく無いのですが、こういうこともできるんですね。勉強になりました。ありがとうございます。hoge <=1.2.3.4 に依存するか hoge >=1.2.3.5 に依存するかのフラグを定義して、それに応じて CPP-Options: を設定する、という手はありますが。
flag hogefuga-new-version
default: True
manual: False
library
... (snip)
if flag(hogefuga-new-version)
build-depends:
hogefuga >= 1.2.3.5
cpp-options: -DHOGEFUGA_NEW_VERSION
else
build-depends:
hogefuga >= 1 && < 1.2.3.5
hogefuga-new-version フラグを cabal build のときに渡して切り替える感じですか?replicate 100 False で作成したリストに対して、mkcs :: Int -> [Bool] mkcs n = [if (x`mod`n==0)then True else False|x<-[1..100]]
turn :: ([Bool],Int) -> ([Bool],Int)
turn (cs.n)
| n == 100 = (nxt,100)
| otherwise = turn (nxt,(n+1))
where
nxt = zipWith xor cs (mkcs n)
xor a b = ((not a)&&b) || (a&&(not b))
main = do
let c = replicate 100 False
print turn (c, 2)
mkcs の定義自体は mkcs n = [x mod` n == 0 | x <- [1..100]]` でも良いですよねands じゃなくて turn ??nxt = zipWith and cs (mkcs n) も nxt = zipWith (&&) cs (mkcs n) かな?False だと何べん (&&) しても False だからなにしたいコードかよくわからないですね...turn :: ([Bool], Int) -> ([Bool], Int) turn = until ((> 100) . snd) (\(cs, n) -> (zipWith xor cs (mkcs n), n + 1))
[Bool] だけだったら普通に畳み込みでいいのかimport Data.Bits main = print $ turn [2..100] (replicate 100 False) turn :: [Int] -> [Bool] -> [Bool] turn ns cs = foldl (\cs' n -> zipWith xor cs' (mkcs n)) cs ns mkcs :: Int -> [Bool] mkcs n = [x `mod` n == 0 | x <- [1..100]]
module Main where
import Control.Arrow ((***))
import Data.Bool (bool)
main :: IO ()
main = print (fromEnum <$> mkes 100)
mkes :: Int -> [Bool]
mkes b = hylo ((:) . mke b) [] phi b
where
phi 0 = Nothing
phi n = Just (succ (b - n), pred n)
mke :: Int -> Int -> Bool
mke b m = hylo xor False phi b
where
phi 0 = Nothing
phi n = Just (m `mod` succ (b - n) == 0, pred n)
hylo :: (a -> b -> b) -> b -> (c -> Maybe (a, c)) -> c -> b
hylo f e phi x = maybe e (uncurry ($) . (f *** hylo f e phi)) (phi x)
xor :: Bool -> Bool -> Bool
xor p q = bool p (not p) q