haskell-jp / questions #88

aDestでMDをMの前に持ってきたらどうでしょうか
ソートしたくないのですね見落としていました。
あとは[Parser Dest]で定義しておいてまとめてasum . map (<* (char ‘=‘))するとか、あるいはラベルとParser Destの対のリストにしてsortするとかでしょうか。モバイルからなので見にくくてすみません。
パーズする対象にものすごく見覚えがあります! 「コンピュータシステムの理論と実装」ですね。
なるほど教科書の誘導に乗らずにつくってらっしゃるんですね。
ソートするしかなさそうですが、とはいえ実際には教科書120ページの表でAMDから逆順に並べるだけでOKではないでしょうか?
もしくは、単にA,M,Dからなる3文字以下の文字列としてパーズしてしまって、それを機械語に変換するときになんとかする、とか。
Parser a 上の <|> 演算で合成されたパーザは、左オペランドのパーザがパーズに成功すると、右オペランドのパーザによるパーズを行わない(or パーズしても結果を捨てる)ように定義されているのではないでしょうか。だとすると、前者のようなコードにはならないと思います。古い記事ですが、
p.178 に少し言及している問題がおこっている思います。
個人的には「もしくは」と書いた方法でやっちゃいました。なぜならAMD を並び替えた ADM とかもぜんぶ有効にしたほうが、言語として親切だと思ったからです。
@maoe
ありがとうございます asum 試してみます

@
たしかにソートでできてしまうのですが
なんというか <|> でつなげる順序に依存しない方法でいきたいのです
AMDについてはこの並びしか許さない方針でいきます

@nobsun
そうなんです
なので各オペランドが = まで見るようにしないといけない
そうすると冗長な記述になるのでどうしよう...というところです
とりあえず
aDest = 
        M   <$ "M"   <* char '='
    <|> D   <$ "D"   <* char '='
    <|> MD  <$ "MD"  <* char '='


aDest = asum . map (<* char '=') $
            [ M   <$ "M"
            , D   <$ "D"
            , MD  <$ "MD"
            ]

と書けました
さらにヘルパー演算子を入れて、だいぶスッキリしました
ありがとうございました
@cashitsuki has joined the channel
import をインライン化したいのですが、何か既存のツールとか、使えそうなライブラリをご存知の方いらっしゃいますか? 例えば

​-- AtCoderPrelude.hs
​module AtCoderPrelude (readUVecLn, parseInt, otherfct) where
​​import Control.Monad.State.Strict (StateT, runStateT)
​​import qualified Data.ByteString as BS
​import qualified Data.ByteString.Char8 as BSC
​​import qualified Data.Vector.Unboxed as VU
​​import qualified Some.Random.Module as M
​​
​​-- | Given a parser, parse a whole input line and give a vector.
​​readUVecLn
​​  :: (VU.Unbox a)
​​  => Int                          -- ^ Expected maximum length 
​​  -> StateT BS.ByteString Maybe a -- ^ Parser
​​  -> IO (VU.Vector a)
​​readUVecLn len parser
​​  = VU.unfoldrN len (runStateT parser) <$> BS.getLine
​
​-- | Skip spaces and control chars, and parse an Int.
​parseInt :: StateT BS.ByteString Maybe Int
parse​Int = StateT
​  $ BSC.readInt . BS.dropWhile (< fromIntegral (fromEnum '!'))
​
​otherfct :: A -> Very -> Useful -> Function
​​otherfct = M.usefulFunction "SomeGoodString"



​-- Main.hs
​import AtCoderPrelude
​
​main :: IO ()	
​main = do
​  n <- readLn
​  vec <- readUVecLn n parseInt
​  print vec

から

​-- Merged.hs
​import qualified Control.Monad.State.Strict (StateT, runStateT) as M3a6f5d
​​import qualified Data.ByteString as M65a7e5
​import qualified Data.ByteString.Char8 as M47ef44
​​import qualified Data.Vector.Unboxed as M3266e4
​​
​​readUVecLn_M2346ed
​​  :: (M3266e4.Unbox a)
​​  => Int
​​  -> M3a6f5d.StateT M65a7e5.ByteString Maybe a
​​  -> IO (M3266e4.Vector a)
​​readUVecLn_M2346ed len parser
​​  = M3266e4.unfoldrN len (M3a6f5d.runStateT parser) <$> M65a7e5.getLine
​
​parseInt_M2346ed :: M3a6f5d.StateT M65a7e5.ByteString Maybe Int
parse​Int_M2346ed = M3a6f5d.StateT
​  $ M47ef44.readInt . M65a7e5.dropWhile (< fromIntegral (fromEnum '!')
​
​main :: IO ()
​main = do
​  n <- readLn
​  vec <- readUVecLn_M2346ed n parseInt_M2346ed
​  print vec

に類するもの(16進ハッシュは適当です)を作れると嬉しい、ということなのですが。
ハッシュ無くても良くて単に1つのソースコードにまとめたいと言うだけなら
AtCoderPreludeからmodule宣言を無くして,
{-# LANGUAGE CPP #-}

#include "AtCoderPrelude.hs"

として
cpphs --noline Main.hs
とすれば1つにまとめられそうです.

Main.hsで新しくimport出来無くなったりする問題はありますが…

ハッシュ付けるならTemplate Haskellで頑張るしか私には思いつきませんね
makeMistakesToLearnHaskellでも :point_down: みたいな感じでその方法を採用しています。そのせいなのかカバレッジがうまくとれなかったりいろいろ難点はありますが、まぁまぁ便利です。
https://github.com/haskell-jp/makeMistakesToLearnHaskell/blob/master/src/imports/external.hs

早く https://github.com/ghc-proposals/ghc-proposals/pull/283https://github.com/ghc-proposals/ghc-proposals/pull/295 がacceptされるといいんですが... :disappointed:
@kimihito has joined the channel
@Ftt has joined the channel
んー…なるほど。AtCoderで何百行あるテンプレートコードを使っていて、手元環境で全体のコードが長いとHIEが走るのが遅くなるんですよね。
• 手元環境では、自作ライブラリ部分については先に別ファイルでコンパイルした状態で走ってもらう
• AtCoder自動提出スクリプトを呼んだときに(ライブラリコードのほうはある程度のpreprocessをコンテスト開始前にやっておく前提でもいいから)オートでそれがインライン化される
なんてことがあると嬉しい、というつもりだったのですが…難しそうですかね。
回答ありがとうございます。
使用したいモジュールも複数あるんですよね、これが…
@shiogai1987 has joined the channel
うーん、なるほど、コンパイル済みであって欲しいと。となると力業でreexportなり衝突するところはrenameするパッケージを書いておいて(で、 stack.yaml とかにextra-depsとしてリポジトリーやtarballのURLを書いておき)、
提出する手前で該当のパッケージのimportを適当なスクリプトでインライン化する、とかですかね...
と、言う理解であってるかな? :thinking_face:
importの最後に自作ライブラリのimportを書いて
提出前にsedとかでimportを置き換えるのが一番現実的…?
@すとまと has joined the channel
はい、そんな感じになると思います。`AtCoderPrelude` だけじゃなくて Numeric.LinearPrimeSieve みたいなモジュールもあるので、単純に sed だとうまくいかないのが難点ですけどね・・・。
まあ、プラグマ、`import`、宣言群という3グループごと流し込む位置を別々にしておけばいいだけのことなので、手作りすればいいだけのことなのですが、 import ... as に対応するのが面倒、とか、色々面倒だなあ、と思ったんですよね・・・。
ある処理の実行時間を測りたい時、Haskellでよく使われるような書き方とかってありますか?

以下のコードはググって書いたコードで、私の実行環境だと、"2000"(プラス数ミリ秒)が出力されますが、この書き方はHaskellで一般的な書き方でしょうか。

millisSinceEpoch :: UTCTime -> Int 
millisSinceEpoch =
    floor . (1e3 *) . nominalDiffTimeToSeconds . utcTimeToPOSIXSeconds

main :: IO ()                                                                                                                                                                                                                                 
main = do
  start <- millisSinceEpoch <$> getCurrentTime
  threadDelay $ 2 * 10 ^ 6 
  end <- millisSinceEpoch <$> getCurrentTime
  print . show $ (end - start)

定番の書き方などがあれば、それに倣った方がよい気がするので、お伺いしたいです。
実行時間を計る場合はNTPなどの影響を減らすためにもgetCurrentTimeよりmonotonicな時計を使うことをおすすめします。clockパッケージのgetTime MonotonicやGHC依存でよいならGHC.Clock.getMonotonicTimeNSecなどがあります。
念のため。遅延IOによって思わぬコードが計測するコードの外側で実行される可能性があります。
必要に応じてdeepseqの rnfevaluate を組み合わせて使いましょう。
http://hackage.haskell.org/package/deepseq-1.4.4.0/docs/Control-DeepSeq.html
今回そこまで求めているかわかりませんが、GHCのプロファイリング機能やベンチマーク用のパッケージ(criterionなど)も調べてみるといいかもしれません。
<@U4M9NJ7QE> さん
ありがとうございます!勉強になります!
getCurrenttimeはNTPの影響を受けるんですね。


<@U4LGTMTMK> さん
貴重な情報ありがとうございます!
まだリンク先は読みきれてないのですが、後ほど拝見しますm(__)m
計測対象が綺麗に切り出せるなら igrep さんの言うように https://hackage.haskell.org/package/criterionhttps://hackage.haskell.org/package/gauge を使って計測するのがいいと思います。個人的にはcriterionと https://hackage.haskell.org/package/criterion-compare で変更前後の結果を比較するのによく使っています。guauge にも似たような機能があるかも知れません。

実際のワークロードの一部を計測したいならmonotonicな時計を使って自分で差分を計算するのが良いと思います。
こんな感じのコードがコンパイルエラーになったんだけど、どうしてなんでしょうか?( DerivingVia 初心者)
  newtype Difference = Difference { unDifference :: Int }
    deriving stock (Eq, Ord, Bounded)
    deriving newtype (Enum, Show, Read, Num, Real, Integral)
    deriving (Semigroup, Monoid) via Sum Int
エラーメッセージはこんな感じです。
src\Numeric\YHSeq\V0201.hs:32:15: error:
    • Couldn't match representation of type 'Int'
                               with that of 'Sum Int'
        arising from the coercion of the method '<>'
          from type 'Sum Int -> Sum Int -> Sum Int'
            to type 'Difference -> Difference -> Difference'
      The data constructor 'base-4.12.0.0:Data.Semigroup.Internal.Sum'
        of newtype 'Sum' is not in scope
    • When deriving the instance for (Semigroup Difference)
   |
32 |     deriving (Semigroup, Monoid) via Sum Int
   |
データコンストラクターの import で結果が変わりました。

{-# LANGUAGE DerivingVia                #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module A where

-- import           Data.Monoid (Sum) -- 同様のコンパイルエラー
import           Data.Monoid (Sum (Sum)) -- コンパイルできる

newtype Difference = Difference { unDifference :: Int }
  deriving stock (Eq, Ord, Bounded)
  deriving newtype (Enum, Show, Read, Num, Real, Integral)
  deriving (Semigroup, Monoid) via Sum Int

試してから気付きましたが、よく見るとエラーメッセージに

The data constructor ‘base-4.12.0.0:Data.Semigroup.Internal.Sum’
of newtype ‘Sum’ is not in scope
って書いてある通りですね。
データコンストラクターがないと Sum Int の中身が Int だと知ることができない、って感じだと思われます。
@maoe
ありがとうございます!
ここに質問を書こうと GitHub 上のソースコードへのリンクを調べてたらバグが分かった
ありがとうございます
ありがとうございます。エラーメッセージに乗ってるの見落としてましたね……
@imura has joined the channel
@Henri/Saks has joined the channel
初めまして、左も右もわからないほど、プログラミング歴がかなり浅いです。早速ですが、質問に答えてくださると嬉しいです。
HaskellのIDEを構築?したいのですが、どうすれば良いのでしょうか?
まず「どれもうまく動けばラッキー」ぐらいの気持ちで考えてください。
JetBrains や Microsoft 並みの IDE はおそらく現状ありません。
その上で今一番活発なのは Haskell Language Server です。
https://github.com/haskell/haskell-language-server
これはエンジンのみでインターフェースは VS Code や Vim・Emacs などお好きなエディターから接続します。
他には IntelliJ IDEA のプラグイン(Haskforce など)や Emacs 用の拡張(Happy Haskell Programming)などがあります。

ちなみに自分は VS Code でシンタックスハイライトのみで開発しています。HLS は構築したのですがうまく動きませんでした。
Emacs で HHP のときもあります。
なるほど、、、そんなにまだ充実しては無いのですね。。。
ありがとうございます!
私は未だ使用してないのですが、Visual Studio Code上で、Haskell Language Server(HLS)拡張を使う環境が急速に整備されている最中です。 この夏くらいには良い感じになると言われています。
ちょうど今週に、HLSの静的なバイナリを自動でインストールする機能の提供が始まったところです。

開発状況の参考情報は以下です。
https://mpickering.github.io/ide/posts/2020-07-10-ghc-libdir.html
https://www.reddit.com/r/haskell/comments/hx0vs8/haskell_language_server_static_binaries_and/
インストールや操作画面イメージが以下で紹介されています。
https://twitter.com/meeple_/status/1286046745076670465
HLS のバイナリー提供期待ですね
@dfordivam has joined the channel
@ has joined the channel
Yutaro Sakamoto
@Yutaro Sakamoto has joined the channel
つまらない質問ですが、 head についてお聞きしたいです。
なぜ head は最初の要素だけなんでしょうか? 関数型言語には利用必須ですが、`head` を`head :: [a] -> Int -> [a]` のような形にして、`last` があるので headの内部実装をそのまま で first を作ってもよかったんじゃないかと思いました。
頭はひとつで尻尾は長いからみたいな理由で慣習的にそうなったんでしょうか?
似たような実装の初出がどの言語で出来たかとか知ってらっしゃる方がいるのであればそちらも教えていただきたいです。
Linuxコマンドの方も一行目のみとかではないのでさらに疑問になりました。
初心者で無知なところも多いと思いますが、よろしくお願いします。