haskell-jp / questions #15

何故正規表現を使いたく無いかよく分かって無いですが,ワイドルカードや許容文字が複数ある場合などは, regex-applicative-textregex-tdfa などをよく使います
そういえば,ややこしそうなのであまり追ってなかったんですが,現状 Typeable のインスタンス解決が破綻してる件ってどうなってるんでしょうか? DeriveDataTypeable はお亡くなりになる感じなのか,それともderiving自体は現状でも書いておくべきなんでしょうか?
GHC Wiki()によれば,まだ結局Long-term solutionが見つかってなくて, everything is Typeable でderivingされたものは無視するという方針でいく感じですかね? そうなると現状, AutoDeriveTypeable が有効になってる前提で書くのがいいのか, DeriveDataTypeable でいちよderivingを書いていくのがいいのかどっちなんですかね?
System.Process.procにShift_JISで引数を渡す方法はありますか?
とあるELFバイナリがShfit_JISで文字列引数を受け取るのでShfit_JISで文字列引数を渡して起動する必要があります
IConv.convert "UTF-8" "SHIFT-JIS"でStringをShift_JISのByteString文字列にすることは出来たのですが
procが受け取るのはUCS-4の集まりであるStringなので詰みました
とりあえずData.ByteString.Lazy.Char8.unpackでStringにしてみたのですが普通に文字化けしました
setForeignEncodingしてみれば良いのではという助言を貰って
https://twitter.com/mod_poppo/status/968397772301873152
latin1やchar8を設定してみたのですが文字化けするのは変わりません
procにはShift_JISの文字列引数を渡すことは出来ないのでしょうか?
Shift_JISを使うにはSystem.Posix.Process.ByteStringを使うしか無かったりするんでしょうか
sortがうまくいかず困っています。
https://github.com/wat-aro/hs-issues/issues/2

プログラミングElixirにあったGitHubからIssueを取ってきてテーブル表示するコマンドラインツールをHaskellで作ろうとしているんですが、取得したデータのリストをソートしようとして、なぜか一番上に来るはずの要素が一番下に来てしまいます。そのデータ以外は期待した並び順になっていてよくわかりません。
これなにがおかしくてこうなってしまうのでしょうか?
https://github.com/wat-aro/hs-issues/blob/master/app/Main.hs
自己解決しました。foldrで出力するときに先頭の要素を最後に持ってきていた。
多分そうだと思います。
:confused: .oO(typed-processを勧めようかと思ったけど、結局同じ問題があるな… 提案してみようかしら…)
ググったらRustをFFIするサンプルコードが結構でてきてホクホクしてきた
これ使おう
よく考えたらコネクション保持しないといけないからそのままじゃだめだった
@VoQn has joined the channel
@tosainu has joined the channel
@ has joined the channel
@ has left the channel
QuickCheckを使ってライブラリのテストを書く時、 instance Arbitrary ってライブラリ側に書くのとテスト側に orphan instance として書くの、どちらがいいんでしょう。
公開して有用ならライブラリ側に書き、公開するつもりがないならテスト側に書けばいいでしょう。
テスト側に書いたら {-# OPTIONS_GHC -fno-warn-orphans #-} を書いておきます。
imperative-edslSystem.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"


https://github.com/emilaxelsson/imperative-edsl/blob/master/src/System/IO/Fake.hs
それは IO をテストする他の方法という意味ですか?それともfakeIOの他の使い方という意味でしょうか?
IO () をテストする他の方法という意味ですー。
fakeIO がどういう仕組みでやっているのかは存じませんが(もしかしたら実質同じパターンかも)、 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 を実質純粋な関数として維持できます。
あとは EnvReaderT を経由して渡すようにすれば、見かけとしてもバッチリになるでしょう。
合わせて読みたい: https://www.fpcomplete.com/blog/2017/07/the-rio-monad

あと、標準出力にしか対応していなくて悩ましいですが、全く違うアプローチとして、

http://syocy.hatenablog.com/entry/haskell-library-2016#%E3%83%86%E3%82%B9%E3%83%88

で触れている、 silently パッケージというのもあります。
こちらは stdout を再オープンして書き換える、という大胆不敵なやり方をとっています。
C++で書かれたライブラリのバインディングを作る際のオススメの方法ってありますか?
https://github.com/wavewave/fficxx が少し気になっているのですが、使ったことある人がいたら、感想を知りたいです。
「テスト可能な形で入出力を切り離す」という目的ならば、Conduitを使うのもありかな、と思います

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
@ has joined the channel
CPP GHC拡張の MIN_VERSION_* で4桁を指定する方法ってないですよね?
具体的には hoge-1.2.3.4hoge-1.2.3.5 で振る舞いを分ける方法って無いですよね?
そのマクロは3桁までしか扱わないので、できないと思います。PVP compliantならば最後の桁の違いによってユーザから見えるAPIの違いはないはずという考えだと思います。
やっぱそうですよね....
@igrep ありがとうございます。
fakeIO の実装は silently にインスパイアされたものなので、仕組み自体は silently に近いです。

Env を使った依存性注入パターンでは、例えば通常 putStrLn を使って出力している箇所を Envprint に差し替える必要がありそうですね。

このやり方に近い感じですか?
https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/

標準入出力だけしか考えてなかったので、参考になりました。ありがとうございます。
このやり方に近い感じですか?
https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/

型クラスはimplicit parameterなわけですから、 Env を implicit に渡していると考えれば近いかと思います。
個人的には Env は直接渡した方が、型クラスより柔軟でお勧めです。
@as_capabl 今回の目的は テスト可能な形で入出力を切り離す というよりは、既存のコードをできるだけ変更せずにテストするためにはどうしたら良いのかな?という感じでした。 (ちゃんと明示してなくてすみません。)

Conduit あまり詳しく無いのですが、こういうこともできるんですね。勉強になりました。ありがとうございます。
一応、.cabalで hoge <=1.2.3.4 に依存するか hoge >=1.2.3.5 に依存するかのフラグを定義して、それに応じて CPP-Options: を設定する、という手はありますが。
ちょうど同じようなこと書こうとしてましたw
参考までに。

patchlevel より下の桁で試してないので勘で物を言っていますが、cabal の flags ソルバで cpp-options を切り替える方法はどうでしょうか?
build-depends の条件が満たされない場合は、cabal のソルバが manual ではない flag の値を反転して条件が満たされるまで試すので、cpp-options で適当なマクロを define しておけば CPP マクロで参照できる気がします。

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 のときに渡して切り替える感じですか?
manual: Falseだとcabalが勝手にon/offを切り替えてくれます
おぉ、ダメだったら勝手に切り替えてくれるんですね。
ありがとうございます、試してみます。
https://www.haskell.org/cabal/users-guide/developing-packages.html#pkg-field-flag-manual
どうやら、stack ではこの方法が出来無いようです(できなかった...)
https://github.com/commercialhaskell/stack/issues/2197
stack 使うのであれば、stack.yaml の resolver と extra-deps から hoge のバージョンが一意に定まるはずなので、stack.yaml の flags にどちらかを明示しておけば良い気がします。
replicate 100 False で作成したリストに対して、
mkcs :: Int -> [Bool] 
mkcs n = [if (x`mod`n==0)then True else False|x<-[1..100]]

を`n<-[1.100]`でandを取りたいときのコードを以下のように書いてみたのですが、これよりスッキリとした書き方はないでしょうか?
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 だからなにしたいコードかよくわからないですね...
説明を簡略化しようとして,いろいろミスっていました
ands -> turn
and -> xor とします.
あー xor か
うーん、すっきりかどうかは分からないけど
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]]
助言ありがとうございます
畳み込みがまだ慣れてないのが,ネックになってそうなので,勉強がんばります
もともとの問題を解くだけなら、100 という1つの整数からリストの構成を1回だけにするようなコードも書けるには書けますね。

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