haskell-jp / beginners #14

@y101172y101172 has joined the channel
@h.grasshopper2 has joined the channel
@yuuki.i.cosmos has joined the channel
@ssktisi78 has joined the channel
@erniogi.0 has joined the channel
@k.marumaru524 has joined the channel
@hal2cilu has joined the channel
@paulpoiu123 has joined the channel
@mpenz.m_0 has joined the channel
jimi.woodstock1995
@jimi.woodstock1995 has joined the channel
@okinakahiro has joined the channel
@kaz45684 has joined the channel
@takumi.shiratori has joined the channel
まだ基本文法で感動してるような段階の初心者なんですが、どうしても経験者でないとわからないことをお聞きしたいです。GitHubでチラチラとHaskellで書かれたリポジトリを見てみたら、なんとなく全体的に確かに他の言語のものより短くプログラムが書かれている気がします。やはり、Haskellを使用すると他の一般的な言語よりも短いコード量になる、というのはあるのでしょうか?
確かに比較的短く書くための機能はたくさんありますね。まぁ、そのあたりの機能を使いすぎると短すぎて逆に分かりづらい、みたいな状況に陥りがちなんで一概にいいとは言えませんが... :sweat_smile:
なるほど。初心者の段階である程度学んでいった先にどんな感じの世界が待ってるのかがわかってたほうがやりやすいので、助かります!
@tooppoo56 has joined the channel
kimura.masatoshi74
@kimura.masatoshi74 has joined the channel
Reminder:
beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。
Haskell-Beginners ML や IRCの#haskell-beginners や RedditのMonthly Hask Anythingのような位置づけを意図しています。

beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。
• それはくだらない質問だ → くだらない質問など無い
• その質問は以前にもあった → 質問者はそんなこと知らない
• Google検索せよ → 検索できないから質問している
beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。
: とは何のことですか。
• タプルとは何ですか。
@mzkxkze has joined the channel
入力処理で、1回の入力単位が1行であれば、hGetLine hdl でよいのですが、
1回の入力単位が複数の改行を含み特定の文字列(たとえば"\n>>> ")が出現するまで、
という場合どう書けばいいでしょうか。
とりあえず1文字ずつ読むしかないかなぁ。
こういうことですか

import           

hGetLineUntil :: Handle -> String -> IO String
hGetLineUntil hdl marker = go ""
 where
  go accum = do
    line <- hGetLine hdl
    if line == marker
      then return accum
      else go $ accum ++ line ++ "\n"


main :: IO ()
main =
  print =<< hGetLineUntil stdin ">>>"
ああ、説明不足でした、特定の文字列というのは或る種のプロンプトであることを想定していまして、
プロンプトの後は一文字も来ていない時点で取得したいのです。
プロンプトは来ていても、改行文字は来ないので、hGetLineではプロンプトが得られない状況です。
とりあえず、書いてみたのですが、入出力が不得意すぎて、なんだかなコードのような気がしています。
hGetUntil :: Handle -> String -> IO String
hGetUntil h str = do
    { eof <- hIsEOF h
    ; if eof then return ""
      else do
          { c <- hGetChar h
          ; if c == head str then (c :) <$> getStr (tail str)
            else (c :) <$> hGetUntil h str
          }
    }
    where
        getStr []     = return ""
        getStr (c:cs) = do
            { eof <- hIsEOF h
            ; if eof then return ""
              else do
                  { c' <- hGetChar h
                  ; if c == c' then (c' :) <$> getStr cs
                    else (c' :) <$> hGetUntil h str
                  }
            }

やりたいことはこういうことだったりしますか?
{-# LANGUAGE OverloadedStrings #-}

module Main where

import qualified              as S
import qualified Data.ByteString.Char8 as BS
import           Data.IORef            (IORef, newIORef, readIORef, writeIORef)
import           System.IO.Unsafe      (unsafePerformIO)

mkBuffer :: IO (IORef BS.ByteString)
mkBuffer = newIORef ""

hGetUntil :: S.Handle -> IORef BS.ByteString -> BS.ByteString -> IO BS.ByteString
hGetUntil h ref marker = do
  prevRemain <- readIORef ref
  (item, remain) <- go prevRemain
  writeIORef ref remain
  return item
 where
  go prevRemain =
    if BS.null bs2
      then do
        mBs <- getBs prevRemain
        case mBs of
          Nothing     -> return (prevRemain, "")
          Just remain -> go remain
      else return (bs1, bs3)
   where
    (bs1, bs2) = BS.breakSubstring marker prevRemain
    bs3        = BS.drop (BS.length marker) bs2
  getBs :: BS.ByteString -> IO (Maybe BS.ByteString)
  getBs prev = do
    isEOF <- S.hIsEOF h
    if isEOF
      then return Nothing
      else do
        True <- S.hWaitForInput h (-1)
        Just . BS.append prev <$> BS.hGetNonBlocking h 1024

main :: IO ()
main = do
  buf <- mkBuffer
  print =<< hGetUntil S.stdin buf "\n>>>"

(IORefは必須ではないですが、どこかに情報をためて受け渡したりする必要があります)
REPLのラッパーを書こうとしいます。*ユーザーの入力(改行終端)ごと*にREPLからの出力(プロンプト文字列終端)を表示するものです。
模式的には、以下の(1)〜(3)の要素を繋げてループにする(あるいはそれぞれをループにしてから繋げる)イメージです。

(1)  (stdin & getLine         ) >>= (INPUT-FILTER  >>> hPutStrLn oh1   )
(2)  (ih1   & hGetLine        ) >>= (REPL          >>> hPutStr   oh2   )
(3)  (ih2   & hGetUntil prompt) >>= (OUTPUT-FILTER >>> hputStr   stdout)

@winterland1989 has joined the channel
@kshiva1126 has joined the channel
@channel
すごいH本 第1版 p104で、
string2digits::String->[Int]
string2digits = map digitToInt .filter isDigit …(1)
とあり、

*Main Data.Char> filter isDigit "4545-45345"
"454545345"

*Main Data.Char> map digitToInt "454545454343"
[4,5,4,5,4,5,4,5,4,3,4,3]

となるまでは分かるのですが、なぜ(1)のように書けるか
分からず、解説をお願いしたいです。
(関数合成(.)を使うと、"digitToInt .filter isDigit"がひとまとめになって、mapした各要素に適用されるイメージのため)
関数適用の優先度が最も高いので、
map digitToInt . filter isDigit

は分かりやすいように括弧を付けると
(map digitToInt) . (filter isDigit)

と評価されるからです!

逆に
“digitToInt .filter isDigit”がひとまとめになって、
を実現したい時は
map (digitToInt . filter isDigit)

と明示的に書く必要があります:eyes:
map digitToInt . filter isDigit(map digitToInt) . (filter isDigit) と同じです。関数合成 f . g は加算 m + n と同じ二項演算子なので、そうなります。
@stat.yukiyama has joined the channel
@lotz / @hexirp
ありがとうございます
Haskellでヒアドキュメントを実現する一番良い方法は何でしょうか
たとえば、
1から100までの連番それぞれに対して、
その番号を含んだ文書を、計100通り出力したいです。
シェルスクリプトやPerlでもよくある処理なので難しいことではないと思っていたのですが、
Haskellでしようとすると詰まってしまいました。

 *試したこと*

• *String Gapを使う方法*
⇒正しくできることもありましたが、下記のエラーがでて動かないこともありました。
`lexical error in string/character literal at character '\n'`

改行の位置やインデントの問題なのかもしれませんが、どこを見れば正しいやり方が把握できるのかがわからなかったです。

• *ライブラリを使う方法*
⇒stackageでヒットしたheredocというライブラリ heredoc

では、変数を埋め込むことが出来なさそうな感じでした。
また、変数を埋め込むことのできるライブラリ  heredocs

はstackに載っておらず、stackで作成したプロジェクトへの導入の仕方がわからず敬遠している状況です。

いいやり方をご存じの方、教えていただきたいです。
⇒正しくできることもありましたが、下記のエラーがでて動かないこともありました。
これは具体的にどういうコードを書いたのか気になりますね。

stackで作成したプロジェクトへの導入の仕方がわからず
stack.yaml に extra-depsという項目があるのでそこに追記してください。
書き方は stack initstack new などで生成したstack.yamlにコメントアウトされています。
あと、それでなくともとりあえず cabal ファイル(あるいは package.yaml があるなら package.yaml)に追加すれば stack build の際「こうやって追記してね!」って教えてくれるはずです。
@hiro.1nakanishi has joined the channel
@harupiyo has joined the channel
benzene.chlorobenzene
現在VSCode+stackで開発をしています。個人的な好みでVimも使いたいのですが、ghcupはHLSのインストールもやってくれると聞き、移行を考えています。stackとghcupを同一の環境にインストールするとき不都合はあるでしょうか
原則stackは独自の領域にGHCなどをインストールするので、干渉するなど大きな問題はないと思います。
強いて言えば、独自の領域にインストールする関係上二重にインストールすることになるから結構容量を食うという点でしょうか。stackに --system-ghc オプションを追加すれば、 PATH からGHCを探すようになるので多少緩和されるはずです。(stackのconfig.yamlでも --system-ghc 相当の設定があった気がしますが忘れました。悪しからず)
@gloel.loe2 has joined the channel
質問なのですが、take 10 [1..] ++ (take 8 [10,9..]) を()を使わず $ で書くようなやり方はあるのでしょうか
もしあるなら教えていただければ幸いです
少なくとも、今回の例はかっこ要らなそう
Prelude> take 10 [1..] ++ (take 8 [10,9..]) 
[1,2,3,4,5,6,7,8,9,10,10,9,8,7,6,5,4,3]
Prelude> take 10 [1..] ++ take 8 [10,9..]
[1,2,3,4,5,6,7,8,9,10,10,9,8,7,6,5,4,3]
ほんとですね!何か勘違いをしていました:pray:
ありがとうございます
@ide_0109 has joined the channel
eagle.hokkaido2317
DB用のdockerコンテナを立ち上げた状態で,Servant用dockerイメージを作成するべく
docker build -t servant-app . コマンドでdefineTableFromDB'を呼ぶとDBコンテナが見つからずにSQL error でコンパイルに失敗します.
ローカルで同一のServant appをbuildするとlocalhost:5432でDBコンテナにアクセス成功するのですが,どのように設定すれば良いかご存知の方いらっしゃいますか?
なお,servant側でのdb設定用のyamlは以下です
port    : _env:DBPORT:"5432"
host    : _env:DBHOST:"localhost"
user    : _env:DBUSER:"postgres"
dbname  : _env:DBNAME:"postgres"
pass    : _env:DBPASS:"mypassword"
sslmode : _env:DBSSLMODE:"disable"
@yf.vermilion has joined the channel
Reminder:
beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。
Haskell-Beginners ML や IRCの#haskell-beginners や RedditのMonthly Hask Anythingのような位置づけを意図しています。

beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。
• それはくだらない質問だ → くだらない質問など無い
• その質問は以前にもあった → 質問者はそんなこと知らない
• Google検索せよ → 検索できないから質問している
beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。
: とは何のことですか。
• タプルとは何ですか。
「すごいH本」の72〜73ページでわからないことがあります.
ghci> let listOfFans = map (*) [0..]
ghci> (listOfFans !! 4) 5
20

と書いてあり,実行すると実際に20が出力されるのですが,`[0..]`で得られるリストは`[0, 1, 2, 3, 4, 5, 6, ...]`ですよね?`(listOfFans !! 4)`ではリスト4番目の要素の3が戻り値になると思ったのですが,なぜ4なのでしょうか.
初歩的な質問ですが教えていただけると幸いです.