haskell-jp / questions #94

昔の GHC だと、UNPACK で Strict な小さな値は、ポインターではなく即値が格納されるようになります。
しかし、現在の GHC は UNPACK は必要なく、Strict で小さければ、既値が格納されます。
unpackされるかどうか自動的に判断されるということですね、ありがとうございます!
@hatsugai has joined the channel
yusuke.nakayama1218
@yusuke.nakayama1218 has joined the channel
質問させてください :raising_hand:
vector-sizedの型レベルで長さを持つVectorの以下のような型クラスのインスタンスを定義したいのですがやり方が分からず困ってます :cry:
うまいやり方を知っている or 一緒に悩んでくれる人がいたらコメントしていただけると嬉しいです :pray:
(動かないですが書きたいコード)
import qualified Data.Vector.Sized as V

class Raccum a where
  raccum :: a -> a

instance Num a => Raccum (Vector 1 a) where
  raccum = id

instance Num a => Raccum (Vector m a) where
  raccum v = V.cons (V.sum v) (raccum $ V.tail v)

一応以下のように試行錯誤したのですが成功せず、、
Vector n a の関数実装に Vector (n-1) a の関数を使っているところの型推論がうまく行かないので
• ↑を解決するためにUndecidableInstancesを使って(あまり使いたくない…)型クラス制約を (Num a, m ~ (1+n), Raccum (Vector n a)) => のように変更するとコンパイルは通るが実行時に Overlapping instances で怒られる
• ↑仕方ないので以下のように一つの実装にまとめると Couldn't match type '1' with '0' arising from a use of 'raccum' と怒られる
instance (KnownNat m, KnownNat n, Num a, m ~ (1+n), Raccum (Vector n a)) => Raccum (Vector m a) where
  raccum v
    | m == 1    = v
    | otherwise = V.cons (V.sum v) (raccum $ V.tail v)
    where m = natVal (Proxy @m)
※`raccum` を実装したいわけではなくこの例のようにGHCの型レベル自然数に対して帰納的に型クラスのインスタンスを実装する方法を知りたいと思っています
:memo: こう書けても良さそうなんですがこれは Illegal type synonym family application 'n + 1' in instance と怒られるんですよね、、
instance (Raccum (Vector n a), Num a) => Raccum (Vector (n+1) a) where
  raccum v = V.cons (V.sum v) (raccum $ V.tail v)
https://qiita.com/mod_poppo/items/3a37424d299a9f71b757 に書いたように、GHCのNatは帰納的な定義ではないので、真っ当な方法でそういう関数を定義することはできません。
unsafeな手段を厭わないのであれば、
data NatCons (n :: Nat) where
  Zero :: NatCons 0
  Succ :: (KnownNat n, m ~ (1 + n)) => Proxy n -> NatCons m

{-# NOINLINE natCons #-}
natCons :: KnownNat n => Proxy n -> NatCons n
natCons proxy = case sameNat proxy (Proxy :: Proxy 0) of
                  Just Refl -> Zero
                  Nothing -> case someNatVal (natVal proxy - 1) of
                               SomeNat proxy' -> unsafeCoerce (Succ proxy')

という補助関数を用意してやれば自然数が0か後続者かで場合分けできるようになります。(完全なコードは https://gist.github.com/minoki/08b5825e249ae5642a6236a5f5adf702
試してませんが、自分でunsafeなコードを書きたくないのであれば、singletonsとcompiler pluginの組み合わせでどうにかできるかもしれません。
Nat自体が帰納的に定義されてないことに起因してますよね、、
調べてる時にこちらの記事も読ませていただきました! :pray:

実装教えていただきありがとうございます!! unsafeな方法でも動かす方法が全く思いつかなかったのでめちゃくちゃありがたいです :innocent:

singletons + pluginですか :memo:
singletons 使ったことがないので考える良い機会になりそうです :arigatougozaimasu: :pray:
singletons + compiler plugin を使った版も書いてみましたが、結局痒いところに手が届かなくてunsafeCoerceを使ってしまう羽目になりました https://gist.github.com/minoki/08b5825e249ae5642a6236a5f5adf702#file-sing-hs
(更新:ghc-typelits-knownnatを入れた意義が微妙なことになっていたので直しました)
すごい!めちゃくちゃ勉強になります :pray: :arigatougozaimasu:
本来書きたかったコードも頂いたコードを参考に無事実装できました :pray: :pray: :pray:
@kzk0693 has joined the channel
HakyllでBinaryのインスタンスじゃないもののsnapshot的なものは取れないんでしょうか。LucidのHtmlTのコンストラクタがエクスポートされてなくてStandaloneDerivingでもBinary(Generic)のインスタンスにできず...
本当にsnapshot取りたかったのは取りたかったのはタイトルの部分ですよね? タイトルだけsnapshot取って解決
Monad TransformerをBinaryのインスタンスにしてシリアライズするのは原理的に難しそうなので、やるとしたら一旦生の文字列(TextでもByteStringでも)に変えるしかなかったのではないかと
リストを codensity 変換したものって forall r. m r -> (a -> r -> m r) -> m r でいいんですよね。これってモナドにするときに Monad m が必要ということで合ってますよね?
https://stackoverflow.com/questions/45334985/when-to-use-cps-vs-codensity-vs-reflection-without-remorse-in-haskell によると、これは「 codensity 変換」ではなく「 CPS 変換」なようです。
mはなくてもよく、foldrにリストを部分適用した型になります。もしLogicTのようにモナドを埋め込むなら、`(a → m r → m r) → m r → m r`ですね
ありがとうございます!
michaeleverydaysunday
@michaeleverydaysunday has joined the channel
@nearestministop has joined the channel
amazonkaでS3へファイルアップロードする処理について質問です。
https://github.com/brendanhay/amazonka/blob/develop/examples/src/Example/S3.hs
こちらのexampleを参考に、下のようなコードで手元でアップロードできることを確認しました。
createSourceFile :: IO ()
createSourceFile = do
    writeFile "input.txt" "This is a test."

s3Upload :: IO ()
s3Upload = do
    createSourceFile
    logger <- newLogger Debug stdout
    env <- newEnv Discover <&> set envLogger logger . set envRegion Tokyo
    runResourceT . runAWST env $ do
        body <- chunkedFile defaultChunkSize "./input.txt"
        void . send $ putObject (BucketName "test-bucket") (ObjectKey "output.txt") body

chunkedFile という関数を噛ませていることから、Streamに読み込みながらアップロードしているという認識で合っているでしょうか?
amazonka-s3-streaming というパッケージもありますが、Streamにアップロードする上で特にこれは不要でしょうか?
https://github.com/axman6/amazonka-s3-streaming/issues?q=is%3Aissue+is%3Aclosed

教えて頂ければ幸いです。
stack.ymlです。
resolver: lts-13.18

packages:
- .

extra-deps:
- conduit-1.3.4
- amazonka-1.6.1
- amazonka-s3-1.6.1
- amazonka-s3-streaming-1.0.0.1
- http-client-0.5.14
- text-1.2.3.1
- lens-4.17
chunkedFile という関数を噛ませていることから、Streamに読み込みながらアップロードしているという認識で合っているでしょうか?
気になって実装読んでましたがその通りStreamに読み込みながらアップロードしてそうです:eyes:
書いてある通り、

chunkedFile でchunk毎に分割したStream (Chunked ChunkedBody) にして
https://github.com/brendanhay/amazonka/blob/020bc7bde47bb235e448c76088dc44d6cec13e9b/amazonka/src/Network/AWS/Internal/Body.hs#L76-L88

send から最終的には http-conduit の http 関数を利用して送信しています
https://github.com/brendanhay/amazonka/blob/020bc7bde47bb235e448c76088dc44d6cec13e9b/amazonka/src/Network/AWS/Internal/HTTP.hs#L147

amazonka の RqBody の Chunked は http-conduit の RequestBodyStreamChunked に変換されるので
https://github.com/brendanhay/amazonka/blob/020bc7bde47bb235e448c76088dc44d6cec13e9b/core/src/Network/AWS/Data/Body.hs#L168

無事 Stream で送信されてると思います。
Streamでアップロードしたいだけなら amazonka-s3-streaming は不要に見えますね:eyes:
正確に確かめられてはいませんが、どうやらamazonka-s3-streamingが使っているのは、マルチパートアップロード https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/mpuoverview.html という機能のようで、挙げられたサンプルが使用している putObject が呼ぶAPIとはまた別物みたいです。
どうやら使用している Network.AWS.S3 以下のモジュールは、S3のREST APIに対する各APIと一対一に対応しているようでして、`putObject` の定義元である Network.AWS.S3.PutObject は、名前のとおりPutObjectという命令 https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html を呼んでいるものと推測されます。
一方、`Network.AWS.S3.StreamingUpload` が使用しているのは CreateMultipartUploadCompleteMultipartUpload といった、マルチパートアップロードで使用するAPIだからです。
一番確実に確かめるなら、Fiddlerなどのプロキシを使って実際にリクエストを読み、以下の各APIのドキュメントと照合してみることでしょう
• PutObject https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
• CreateMultipartUpload https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html
• CompleteMultipartUpload https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html
あ、長くなりましたがいずれにしてもlotzさんの回答のとおり、`putObject` でもストリーミングアップロード、つまり 一つの リクエストでちょっとずつファイルの中身をアップロードすることはできていると思います。
マルチパートアップロード機能は、とても大きなファイルを 複数の リクエストでアップロードするためのものなので。
なるほど、理解できました!
まだ学び始めで見様見真似といった感じですが、実装も読めるようになっていきたいです。
皆さんありがとうございます。
send から最終的には http-conduit の http 関数を利用して送信しています
なるほど、amazonkaは内部でconduit(ストリーミングライブラリですよね)を使っていたんですね。
@y101172y101172 has joined the channel
質問と直接関係なく恐縮なのですが、ChunkedFileと見て思い出したので共有させていただきます。
現在リリースされているamazonkaのバージョンで、ChunkedFileを使って1mb以上のファイルを扱う際に、次のバグを踏みそうに見えます。ご注意下さい。
https://github.com/brendanhay/amazonka/issues/546
ありがとうございました。
S3へのputアップロードとマルチパートアップロードどちらも手元で動作できましたので、会社ブログにまとめました。
確認なのですが、謝辞としてhaskell-jpに相談させて頂きましたと一言追記することは良いでしょうか?
https://dev.classmethod.jp/articles/haskell-s3-upload/
ChunkedFileを使って1mb以上のファイルを扱う際に、次のバグを踏みそうに見えます。
ありがとうございます、このバグしっかり踏みましたw
developに修正はマージされているようですが、リリースはいつになるのか・・・:thinking_face:
後で追記しようと思っています。
確認なのですが、謝辞としてhaskell-jpに相談させて頂きましたと一言追記することは良いでしょうか?
問題ないと思います :ok_woman:
僕もよく記事にHaskell-jpに質問した旨の謝辞を書いてますー
あまり見やすくはないのですが、slack-logの発言のリンクまで張っていただけると特に助かります。
この辺: https://haskell.jp/slack-log/html/C5666B6BB/94.html#message-1609654731.221800
こういうのあるんですね!
ご丁寧に教えて頂きありがとうございます。
おわりに の中に追記させて頂きました!
@h.grasshopper2 has joined the channel
cabal init したときに、作成されるsrc/MyLib.hs などの雛形ファイルをカスタマイズするにはどうすればいいのでしょうか。stack を使う場合は、~/.stack/config.yaml でtemplateファイルを指定して、そのtemplateファイルを編集すればよかったのですが。
Distribution/Client/Init.hs にハードコーディングされてるのかな。。。
全然回答になってませんが、今時はGitHubのテンプレートリポジトリーを使った方がいいのかも知れませんね。
ああ、それがよさそう。ありがとうございます。
https://github.com/kowainik/summoner summonerというツールが行儀のいい設定を用意してくれるのでおすすめです
ありがとうございます。みてみます。
@yuuki.i.cosmos has joined the channel
@ssktisi78 has joined the channel
@erniogi.0 has joined the channel
https://github.com/theam/aws-lambda-haskell-runtime
こちらのリポジトリを眺めていて
I believe that we should stop using `package.yaml` given that now Stack is deprecating it and Cabal supports common stanzas
というやり取りを見てちょっとびっくりしたのですが、今のHaskell開発はStackを使わない流れになっているのでしょうか?
Stackのリポジトリを見ると開発は普通に継続してるように見えますが、Haskellerの肌感を聞きたいです。
https://github.com/theam/aws-lambda-haskell-runtime/pull/97#discussion_r549287520