haskell-jp / questions #62

うーん、それで No module named 'GHC.Arr' is imported. というエラーはさすがに不自然ですね。。。
試してみましたが、エラーメッセージは変わってるはずですよね?
Module ‘GHC.Arr’ does not export ‘unsafeFreezeSTUArray’

とすると、なるほど、なぜか unsafeFreezeSTArrayGHC.Arr にあるけど unsafeFreezeSTUArray にないってことか。。。
すみません、わざわざ unsafe なAPIを使わなくても freeze 関数というのがあるみたいですね。

import Data.Array.Unboxed
import 
import 

main :: IO ()
main = print arr2

arr2 :: (UArray Int Int, UArray Int Int)
arr2 = runST $ do
  a <- newArray (0,2) 0 :: ST s (STUArray s Int Int)
  writeArray a 0 999
  b <- newArray (0,2) 0 :: ST s (STUArray s Int Int)
  writeArray b 0 999
  -- detail ommitted
  (,) <$> freeze a <*> freeze b
(,) <$> freeze a <*> freeze b ですか! 素晴らしい 有難うございました orz
@kazasiki has joined the channel
@shsato.t.t.n has joined the channel
@tomozx8 has joined the channel
cryptonite速くなりましたね。
---
MagicHaskellerを動かしたいのですが、
GHCAPIをつかっているのですが、モジュールのロードがうまくいかず、困っています。
http://hackage.haskell.org/package/MagicHaskeller-0.9.6.7/src/MagicHaskeller/SimpleServer.hs
このファイルのprepareGHCAPI です。

stackでlts-3.22でビルドはできたのですが。
MagicHaskeller/Minimal.hsがロードできないというエラーで落ちてしまいます。
どなたかノウハウがあれば教えていただきたいです。

手順通りcabalでコンパイルするとNetworkモジュールのロードでこけます。自分の環境のせいかどうかもよくわからないです。
また、たくさんの関数を推論させたいのでポータル(
ローカルで実行したいです。WEB サーバーにする必要はありません。
正直なところGHC APIをあまり使ったことがないのでわかるかどうかも不明ですが、再現するリポジトリーを上げていただけるとサポートしやすいです。
そうしますね。
GHC APIの使い方がわからない問題でデバッグできないですね。
なにかパスを追加とかなのでしょうが
https://circleci.com/gh/junjihashimoto/MagicHaskeller
https://github.com/junjihashimoto/MagicHaskeller
ここで再現するはずです。
ちょっとお待ちください。
ちょっと見た感じだと、GHC API を使ってパッケージデータベースから MagicHaskeller を指定し、Prelude とともに MagicHaskeller.Minimal モジュールと MagicHaskeller.FastRatio モジュールを読み込もうとしてますね。

https://github.com/junjihashimoto/MagicHaskeller/blob/master/MagicHaskeller/SimpleServer.hs#L445
https://github.com/junjihashimoto/MagicHaskeller/blob/master/MagicHaskeller/SimpleServer.hs#L207

エラーになっている原因はたぶん、デフォルトのパッケージデータベースに MagicHaskeller が存在しないためだと思います。

色々とやり方はあると思いますが、Docker で動かすのが一番簡単かもしれません。

FROM ubuntu:bionic-20190307
RUN apt-get update
RUN apt-get install -y haskell-platform

RUN cabal update
RUN cabal install MagicHaskeller-0.9.6.7 --global

###########################
# ghc: 8.0.2
# cabal-install: 1.24.0.2
###########################


良くわかりませんが、動いてる気がします。

$ docker run --rm -it waddlaw/magic-haskeller:0.9.6.7 bash
/# MagicHaskeller -d 5 -t predicates -i
MagicHaskeller/MagicExceller backend server version 0.9.6.7 built with GHC-8.0 at 2019-06-10 07:33:54.682998096 UTC
started at 2019-06-10 07:46:43.917068284 UTC
\f -> ?
f "e" 2 == "ee"
the predicate is f "e" 2 == "ee"




\a b -> concat (replicate b a)

\a b -> replicate b (last (' ' : a))
\a b -> concat (replicate b (reverse a))
\a b -> take b (a ++ a)
\a b -> replicate b (foldr const ' ' a)
\a b -> concat (transpose (replicate b a))
\a b -> concat (replicate (abs b) a)
\a b -> a ++ take b a
\a b -> concatMap (\_ -> a) (replicate 2 b)
\a b -> concatMap (\_ -> a) [b..3]
時間経ってからでアレですが、実現方法思いついたのでコメント

関数Applicativeをネストさせる作戦
(liftA2 . liftA2) (>>) a b "x" "y"


uncurry する作戦
(liftA2 (>>) `on` uncurry) a b ("x", "y")
元々考えていたケースは引数の数が2もあれば3や4もあるので、根本的な間違いの回避にはつながりませんが、いいですね!
ありがとうございます。
助かります。
@merrynewyear6593 has joined the channel
@shibahas.has has joined the channel
@stekitou has joined the channel
@takakuni1118 has joined the channel
@chikusat has joined the channel
レコードのフィールドの関数用法は、値構築子が複数あると部分関数になってよくないので GHC に partial-fields って警告オプションがあるのですが、これだとパターンマッチでしか使っていなくて安全なときでも、そういうフィールドを宣言した時点で警告が出てしまうので、そういうフィールド(もしくはレコードのフィールドすべて)を関数用法をしたときだけ警告が出るようなオプションってありましたっけ?
更新ならあるんですけど、かゆいところに手が届かないですね... :disappointed:
https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates
そういえば更新も危険でしたね
チャンネル違いで恐縮ですが: -Wallに-Wincomplete-record-updatesを含めるproposalはacceptedされたそうです。
https://github.com/ghc-proposals/ghc-proposals/pull/71
英語版 Reddit あたりに投げてみますかね
@tsuzuki357 has joined the channel
読んでみました。まさにこんな感じですね。
スレッドが SPJ の質問で止まっちゃってますね……
Dormantになった提案を強力に前に進める人がいるといいなあと思ったりします。提案者の熱があるうちはいいですが、息切れしてきた時に細かい指摘を拾って提案自体にPRを送ってくれるような。
継続 + IO + 失敗系モナド?な感じのパッケージってあります?
具体的には以下のような関数が欲しいんですけど:
fromMaybeWith :: MonadIO m => m (Maybe a) -> m r -> ContT r m a
fromMaybeWith m e = lift m >>= \case
  Nothing -> ContT (const e)
  Just a  -> ContT ($ a)

fromEitherWith :: MonadIO m => m (Either a e) -> (e -> m r) -> ContT r m a
fromEitherWith m act = lift m >>= \case
  Left e  -> ContT (const $ act e)
  Right a -> ContT ($ a)
副作用を伴うAPIの設計で、「成功時はNothing, 失敗時はJust でエラーを返す」というのはよくあるパターンなのでしょうか?
kafkaのライブラリ(hw-kafka-client)を使っていて、そういう場面に出会いまして、「ああ、そうなのか」と思いつつ、失敗したときのエラーを返すなら、Either を使って、Leftにエラー、Rightは() ? としたほうが自然なのかとも思いました。


https://github.com/haskell-works/hw-kafka-client
sendMessages prod = do
  err1 <- produceMessage prod (mkMessage Nothing (Just "test from producer") )
  forM_ err1 print
ExceptTの中で使うことを考えると、確かに IO (Either e ()) の方が良さそうですね。私もそうします。
フォーク元のhaskakafkaを引き継いでいるようですね。Maybeでエラーを返すのは私もアンチパターンと考えています
これは欲しい!MonadIO制約はいらないような…?
たしかに、 MonadIO いらない!
ぼくがよく IO (Maybe a) で使ってたのでw
yesodを使ってローカルのPostgreSQLのテーブルに保存されたユーザー情報で認証をするようにしたいのですが、うまく行かないので教えて頂きたいです。

使用している環境は以下のような構成です。
yesod-bin version: 1.6.0.3
yesod-auth-hashdb-1.7.1.1
PostgreSQL(11.1)

Yesod.Auth.HashDBのドキュメントを参考にして作業しています。
https://www.stackage.org/haddock/nightly-2019-06-17/yesod-auth-hashdb-1.7.1.1/Yesod-Auth-HashDB.html

[config/models]
--認証用のテーブルとしてclient_authをmigrationで作成
ClientAuth                                                              
	clientId ClientId    
	systemid Int                               
	username Text              
	password Text                                                             
	UniqueClientAuth username                  
	deriving Show      


HashDBのドキュメントのサンプルを元に以下のコマンドで管理用のパスワードを生成し、psqlで手入力で認証情報をテーブルにinsertしました。

> makePassword "admin" 17
"sha256|17|gQ3ny1p2WBkGrxrz5krLXg==|bKJ2rPrKczl3eFjQLRwsZi3OpiVepEH0511ocjcAO98="


データベースの現在の状態です。

 select * from client_auth;
 id | client_id | systemid | username |                                     password                                      
----+-----------+----------+----------+-----------------------------------------------------------------------------------
  1 |         1 |        1 | admin    | "sha256|17|gQ3ny1p2WBkGrxrz5krLXg==|bKJ2rPrKczl3eFjQLRwsZi3OpiVepEH0511ocjcAO98="
(1 row)

username:admin password:admin で認証したいです。

パスワードの正しさをチェックするためにYesod.Auth.Util.PasswordStoreのページを参考にverifyPasswordでも確かめてみました。
https://www.stackage.org/haddock/nightly-2019-06-17/yesod-auth-1.6.6/Yesod-Auth-Util-PasswordStore.html

> verifyPassword "admin" "sha256|17|gQ3ny1p2WBkGrxrz5krLXg==|bKJ2rPrKczl3eFjQLRwsZi3OpiVepEH0511ocjcAO98="
True



HashDBのドキュメントを参考に以下のように書きました。
[src/Foundation.hs]
import Yesod.Auth.HashDB (authHashDB, HashDBUser(..))

instance YesodAuth App where                                
    type AuthId App = ClientAuthId                                
                                 
    -- Where to send a user after successful login                                
    loginDest :: App -> Route App                                
    loginDest _ = HomeR                                
    -- Where to send a user after logout                                
    logoutDest :: App -> Route App                                
    logoutDest _ = HomeR                                
    -- Override the above two destinations when a Referer: header is present                                
    redirectToReferer :: App -> Bool                                
    redirectToReferer _ = True                                
                                 
    authPlugins _ = [authHashDB (Just . UniqueClientAuth)]                                
                                 
instance HashDBUser ClientAuth where                                
  userPasswordHash = Just . clientAuthPassword                                
  setPasswordHash hash clientauth = clientauth { clientAuthPassword = hash } 


開発環境を起動
stack exec -- yesod devel

この構成でブラウザを開いてデフォルトのログインフォームが表示される所までは良いのですが、admin:adminの
入力でログインしようとしても“Invalid username/password combination”のメッセージがブラウザ上に表示され、
コンソール側には以下のメッセージが表示されます。

19/Jun/2019:10:01:28 +0900 [Debug#SQL] SELECT "id","client_id","systemid","username","password" FROM "client_auth" WHERE "username"=?; [PersistText "admin"]
POST /auth/page/hashdb/login
  Params: [("_token","7rrCxvYLlw"),("username","admin"),("password","admin")]
  Request Body: _token=7rrCxvYLlw&username=admin&password=admin
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Status: 303 See Other 0.026735s
GET /auth/login
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Status: 200 OK 0.000806s


Paramsで表示されたフォームの入力値も正しいですし、表示されているSQLも正しいように思います。どこをチェックすれば良いでしょうか?
authenticate メソッドが正しく定義できていないのかもしれないですね。
ありがとうございます。皆さんの意見を聞いて安心しました。
どんなユースケースなんすか
例えば、API かなんかで http get でリスト取得、List.lookup 的なのする、Nothing なら pure () して終わり、Just なら別のを http get .... 的な
-- 疑似コード
main :: IO ()
main = evalConstT $ do
  target <- List.lookup isTarget <$> Http.get "" `fromMaybeWith` pure ()
  hoge <- List.lookup (isHoge target) <$> Http.get "" `fromMaybeWith` pure ()
  lift $  (show hoge)
`fromMaybeWith` pure ()

の部分を Logger.warn "not found" とかして終えたりもできますね。
あ、これを一般化できないかなぁと思った次第で
https://haskell.e-bigmoon.com/posts/2018/06-26-cont-param.html
なるほど。自分なら ExceptT と failWith https://hackage.haskell.org/package/errors-2.3.0/docs/Control-Error-Util.html#v:failWith でやりそうなケースですけども、その方が簡潔そうですね!Eitherを伴わない分速いのかも。
noteT じゃなくて failWith でした。修正。