æå°ã§ãé¢å¿ãåé¢ãã
ãã¤ã¯ããµã¼ãã¹ã¢ã¼ããã¯ãã£ã«ããã¦ãããæ§é åããããã¼ã¿ããæ§é ãç¶æããã¾ã¾å¥ã®ã³ã³ãã¼ãã³ãã§ã使ããããªãèªæãåå¨ããã ä¾ãã°ããã¡ã¤ã³ãã¸ãã¯ãæ±ãã³ã³ãã¼ãã³ãã«ãã¦ã¼ã¶ã¼ã®ç¨®å¥ï¼ã²ã¹ããã¡ã³ãã¼ã管çè ï¼ãã¨ããæ¦å¿µããããAPI ãã¼ã¯ã³ã®èªå¯æ å ±ã«å«ãããã¨ãã¦ãAPIãã¼ã¯ã³ã管çããã³ã³ãã¼ãã³ãã¯ãã®æ¦å¿µã«é¢ä¸ãã¹ãã ãããã
// é¢ä¸ããªãå®ç¾© IssueToken(userType: string, expiresAt: Date): Token // é¢ä¸ããå®ç¾© enum UserType { Guest, Member, Admin, } IssueToken(userType: UserType, expiresAt: Date): Token
ããã§å®æã«ãããã¦ãã¾ãã¨ããã¦ã¼ã¶ã¼ã®ç¨®å¥ãã®å®ç¾©ãå¤ãã£ãã¨ãââä¾ãã°æ°ãã«ãã¢ãã¬ã¼ã¿ã¼ãã¨ãã種å¥ã追å ãããå ´åãåæ¹ã®ã³ã³ãã¼ãã³ã両æ¹ã®ãããã¤ãå¿ è¦ã¨ãªã£ã¦ãã¾ããäºææ§ãç¶æããæéãã対å¿æ¼ãã«èµ·å ãããã°ã®ãªã¹ã¯ãçã¾ããããããã¤ã¯ããµã¼ãã¹ã¢ã¼ããã¯ãã£ã®å©ç¹ãæãªãããã
ãã®ããããã¤ã¯ããµã¼ãã¹ã¢ã¼ããã¯ãã£ãæ¡ç¨ããéã¯ãä¸æµã®ã³ã³ãã¼ãã³ãã«ä¾åãã表ç¾ã»æåãå¯è½ãªéãæé¤ãã¹ãã ã Protobufãªã©ã®ã¢ã¸ã¥ã¼ã«ã·ã¹ãã ã使ãã°åä¸ã®ã¹ãã¼ããè¤æ°ã®ã³ã³ãã¼ãã³ãã§ä½¿ãã¾ãããã¨ãã§ããããããéããã°ã³ã³ãã¼ãã³ãéã®ãã¸ãã¯ã®æ¼æ´©ãå ±ä¾åã®æ¸©åºã¨ãªã£ã¦ãã¾ãããã¼ã¿ãä»ã®ã³ã³ãã¼ãã³ãã«æ¸¡ãæã¯ãä¾ããããæ§é åããããã®ã§ãã£ã¦ããä»»æã®æååã¨ã¿ãªãã¦æ±ãã¹ãããããã®å¢ãã§èãã¦ããããã
ãªãªã¼ã¹ããã¦ããªãå¤æ´ãæºã¾ãã®ãé²ãGitHub Actionãtocenboughã
ãã¼ã ã§ã½ããã¦ã§ã¢éçºãããã¨ããä¸åº¦ã®ãªãªã¼ã¹ã«å«ã¾ããå¤æ´ãå¤ããããã¨ã«ãããåä½æ¤è¨¼ã«æéãããã£ãããåé¡ãçºçããæã®åå ç¹å®ãé£ãããªããã¨ãããããããé²ããããtocenboughã¨ããGitHub Actionãä½ã£ããèªã¿æ¹ã¯ãã¡ãããã¨ãããã¼ããã ã
å 容ã¯è³ã£ã¦ã·ã³ãã«ã§ãååã®latest releaseããã®ãã¼ã¸ã³ãããã®æ°ãè¨ç®ãããã®æ°ãé¾å¤ãä¸åã£ããè½ã¡ãã¨ããã ãã®ã¹ã¯ãªããã§ãããããªãåç´ãªã®ã§ãåä½ãææ¡ããã«ã¯ã½ã¼ã¹ãè¦ã¦ããã£ãã»ããæ©ããããããªãã
# GitHubããææ°ã®ãªãªã¼ã¹ã®ã¿ã°ãåå¾ãããããããã«ãªã¯ã¨ã¹ãã®æ°ãã«ã¦ã³ãããèµ·ç¹ã¨ãªã latest=$(curl -H "Authorization: Bearer ${{ inputs.token }}" --silent \ "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r .tag_name) # ã¾ã ãªãªã¼ã¹ãã²ã¨ã¤ããªãå ´åãååã®ã³ãããããã·ã¥ãè¨å®ãã if [ "$latest" == "null" ]; then latest=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi log="$(git log --merges --format="%an %s" "$latest..origin/${{ github.event.pull_request.base.ref }}")" # ãã¼ã¸ã³ãããã®æ°ãæ°ããããã ãã[bot]ãã¤ããã®ã¯å¥ã«ã«ã¦ã³ãããã pr_count=$(echo "$log" | grep -v 'bot]' | wc -l | xargs) bot_count=$(echo "$log" | grep 'bot]' | wc -l | xargs) # PRæ°ã®å®æ°å + botã«ããå¤æ´æ°ããã¹ã³ã¢ãè¨ç®ãã score=$(( $pr_count * ${{ inputs.multiplier }} + $bot_count )) expr="Total weight of unreleased PRs since $latest: ${{ inputs.multiplier }} * $pr_count[PRs] + $bot_count[bot] = $score" # GITHUB_STEP_SUMMARYã«çµæãæ¸ãè¾¼ã echo -e "### tocenbough\n" >> $GITHUB_STEP_SUMMARY if [ "$score" -gt "${{ inputs.threshold }}" ]; then echo "We have too many unreleased changes! $expr" >> $GITHUB_STEP_SUMMARY if [ ${{ contains(github.event.pull_request.labels.*.name, 'cram') }} == "false" ]; then exit 1 fi else echo "$expr" >> $GITHUB_STEP_SUMMARY fi
æå¾ã®ã¹ãããã§åç §ãã¦ãã $GITHUB_STEP_SUMMARY ã¯ãjob summary *1ã®ãã¹ã§ãããããã«ä»»æã®æååãæ¸ãè¾¼ãã¨ãGitHub Actionã®Summaryãã¼ã¸ã«è¡¨ç¤ºã§ããã
ããå®ç¨æ§ãé«ãããããããã¤ãã®å·¥å¤«ãæ½ãã¦ããã
工夫: botã«ããå¤æ´ã®éã¿ã調æ´ãã
dependabotãrenovateã«ããPRã¯æ°ã嵩ã¿ãããããã人ã®æã«ããå¤æ´ã¨åãããã«ã«ã¦ã³ãããããªãå ´åãããããã®ããã multiplier
ã¨ãããã©ã¡ã¼ã¿ãè¨å®ãããã¨ã§ã人ã®æã«ããPRã®éã¿ã調æ´ã§ããã
工夫: ç·æ¥æã¯å¶éãçªç ´ã§ãã
ãcramãã¨ããååã®ã©ãã«ãPRã«ã¤ããã¨ãå¶éãä¸åã£ã¦ããã¨ãã¦ãjobã¯æåãããæ¥ãã§ä¿®æ£ããããã®ãããå ´åã¯æ¬¡ã®ãªãªã¼ã¹ã«ããè¾¼ããã¨ãã§ããã
çµæ
HERP社å ã§æãæ´»çºãªãªãã¸ããªã§åå¹´ã»ã©éç¨ãã¦ã¿ãçµæããªãªã¼ã¹ãµã¤ã¯ã«ãç®ã«è¦ãã¦æ¹åããã(å°å ¥åã®åå¹´éã«æ¯ã¹ããªãªã¼ã¹ééã®å¹³åã¯5.5æ¥ãã4.2æ¥ãä¸å¤®å¤ã¯6æ¥ãã3æ¥ã«ãªã£ã)ã
ãå¤æ´ããã¼ã¸ããããã«ãã¾ãæºã¾ã£ã¦ããå¤æ´ããªãªã¼ã¹ãããã¨ããã¤ã³ã»ã³ãã£ããããªãªã¼ã¹ä½æ¥èªä½ã¯ãã¡ãããããã»ã¹ã®æ¹åããä¿ããããäºæ³ä»¥ä¸ã«å¹æãããããã ã
ãªãªã¼ã¹ã®é »åº¦ãç²åº¦ã«æ©ã¿ã®ãããã¼ã ã¯ã試ãããã.github/workflows
ã®ä¸ã«ä»¥ä¸ã®ãããªè¨å®ãç½®ããã¨ã§å°å
¥ã§ããã
name: tocenbough on: pull_request: types: [opened, reopened, labeled, unlabeled] jobs: cd: runs-on: ubuntu-latest steps: - uses: fumieval/tocenbough@v0 with: token: ${{ secrets.GITHUB_TOKEN }} threshold: 10
SendGridã®Event WebHookãæ¤è¨¼ãã
ã¡ã¼ã«é ä¿¡ãã©ãããã©ã¼ã ã§ããSendGridã¯ãã¡ã¼ã«ã®å°éç¶æ³ãªã©ããå¼ã³åºãå ã®ã¢ããªã±ã¼ã·ã§ã³ã«WebHookã§ä¼ãããã¨ãã§ããã
ã¢ããªã±ã¼ã·ã§ã³ãSendGridãããªã¯ã¨ã¹ããåãåãã«ã¯ãå½ç¶ã¤ã³ã¿ã¼ãããããã¢ã¯ã»ã¹ã§ããããã«ããå¿ è¦ãããããä½ã®èãããªãã«éæ¾ãã¦ã¯ã»ãã¥ãªãã£çã«ä¸å®ã ãããããå®å¿ããSendGridã«ã¯WebHookã«ãã¸ã¿ã«ç½²åãã¤ããæ©è½ããããæ¬ç¨¿ã§ã¯ãã®ä½¿ãæ¹ãç´¹ä»ããã
ã¾ããMail Settings
ãããSigned Event Webhook Requests
ã¨ããé
ç®ã«å
¥ããç»é¢ã®ãã¿ã³ãã¯ãªãã¯ããã¨ãVerification Keyãçæãããé»åç½²åãæå¹ã«ãªããããã¯æã
(HERP)ã®ã·ã¹ãã ã§å®éã«éç¨ãã¦ããéµã§ããã
ãã®è¨å®ãããã¨ã以å¾ã®WebHookã«ã¯ãX-Twilio-Email-Event-Webhook-Signature
ã¨X-Twilio-Email-Event-Webhook-Timestamp
ã®äºã¤ã®HTTPãããã¼ãä»ä¸ããããã¢ããªã±ã¼ã·ã§ã³å´ã¯ãããã使ã£ã¦æ¤è¨¼ããã°ããã
ã¾ããé»åç½²åã«ä½¿ãã¢ã«ã´ãªãºã ãããã·ã¥é¢æ°ãã¡ãã»ã¼ã¸ãç½²åãå ¬ééµãæ´çããã
- ã¢ã«ã´ãªãºã : ECDSA(æéä½ä¸ã®æ¥åæ²ç·ããªã群æ§é ãç¨ããé»åç½²åã¢ã«ã´ãªãºã )
- ããã·ã¥é¢æ°: SHA256
- ã¡ãã»ã¼ã¸:
X-Twilio-Email-Event-Webhook-Timestamp
ã®å¤ã¨ããªã¯ã¨ã¹ãããã£ãçµåãããã® - ç½²å:
X-Twilio-Email-Event-Webhook-Signature
- ASN.1ãBase64ã§ã¨ã³ã³ã¼ããã¦ãã
- å
¬ééµ:
Verification Key
- ASN.1ã§ã¨ã³ã³ã¼ã*1ãããããã«Base64ã§ã¨ã³ã³ã¼ãããã¦ãã
- ç§å¯éµ: SendGridãä¿æãã
é¢ä¿ãæ´çããã¨ããã§ãå ¬å¼ã®ããã¥ã¡ã³ããåèã«å®è£ ãé²ãã¦ãããdocs.sendgrid.com
ã¾ãã¯å¿ è¦ãªã¢ã¸ã¥ã¼ã«ãã¤ã³ãã¼ããã¦ãããããããã¨ä¸¦ã¹ãããæã¯ããã¯ãHaskellã®é¢¨ç©è©©ã§ããã
import "asn1-encoding" Data.ASN1.BinaryEncoding (BER(..)) import "asn1-encoding" Data.ASN1.Encoding (decodeASN1) import "asn1-types" Data.ASN1.Types (ASN1(..), fromASN1) import "base" Data.Proxy import "base" Data.Bifunctor (bimap, first) import "base64" Data.ByteString.Base64 (decodeBase64) import "bytestring" Data.ByteString qualified as B import "bytestring" Data.ByteString.Lazy qualified as BL import "cryptonite" Crypto.ECC (Curve_P256R1) import "cryptonite" Crypto.Error (CryptoFailable(..)) import "cryptonite" Crypto.Hash.Algorithms (SHA256(..)) import "cryptonite" Crypto.PubKey.ECC.Types (CurveName(..)) import "cryptonite" Crypto.PubKey.ECDSA qualified as ECDSA import "x509" Data.X509 (PubKey(..), PubKeyEC(..), SerializedPoint(..))
å
¬ééµãBase64ã¨ASN.1ã§ã¨ã³ã³ã¼ãããã¦ãããã¨ã¯ããã«ããããããã®ä¸èº«ã®ãã©ã¼ããããããã¥ã¡ã³ãã«æ¸ããã¦ããªãã£ããããå°ã
è¿·ããçãããå®é¨ãã¦ã¿ãã¨ããx509ããã±ã¼ã¸ã®PubKey
ã¨ãã¦ãã³ã¼ãã§ãããã¨ãããã£ãã
type PublicKey = ECDSA.PublicKey Curve_P256R1 curve :: Proxy Curve_P256R1 curve = Proxy parsePublicKey :: B.ByteString -> Either String PublicKey parsePublicKey b64 = do -- base64ã¨ãã¦ãã³ã¼ã raw <- first show $ decodeBase64 b64 -- ASN.1ã¨ãã¦ãã³ã¼ã asn1 <- first show $ decodeASN1 BER $ BL.fromStrict raw -- æ¥åæ²ç·ã®ãã©ã¡ã¼ã¿ã®ããªã»ããã¨ãå ¬ééµãåãåºã pubkey <- bimap show fst $ fromASN1 asn1 -- secp256r1ã¨ä»®å®ãã SerializedPoint point <- case pubkey of PubKeyEC (PubKeyEC_Named SEC_p256r1 bs) -> Right bs _ -> Left $ "Unsupported public key: " <> show pubkey -- 座æ¨ããã³ã¼ã case ECDSA.decodePublic curve point of CryptoPassed a -> Right a CryptoFailed err -> Left $ show err
ããã¾ã§æ¥ãã°ããã¨ã¯é¢æ°ãå¼ã³åºãã ããªã®ã§ç°¡åã ãæå·ãé»åç½²åã¯ãå®è£ ãééããã°ãã ã®ã©ã³ãã ãªæååã«ãªã£ã¦ãã¾ããããã©ã¤ãã©ãªã®ããããã¿ãæããããã
verify :: PublicKey -> B.ByteString -> B.ByteString -> B.ByteString -> Either String () verify pubKey timestamp payload signatureBase64 = do -- ç½²åããã³ã¼ããã signature <- either (const $ Left "Malformed signature") pure $ decodeBase64 signatureBase64 -- ECDSAã®ç½²å(r, s)ãåãåºã point <- case decodeASN1 BER $ BL.fromStrict signature of Right [_, IntVal r, IntVal s, _] -> pure (r, s) Right asn1 -> Left $ "failed to decode the signature: " <> show asn1 Left err -> Left $ show err case ECDSA.signatureFromIntegers curve point of CryptoPassed sig | ECDSA.verify -- æ¤è¨¼ãã curve SHA256 pubKey sig $ timestamp <> payload -> pure () | otherwise -> Left "Verification failed" CryptoFailed _ -> Left "ECDSA.signatureFromIntegers failed"
WebHookã®è¨å®ç»é¢ã§ãTest Your Integrationããã¯ãªãã¯ããã¨éããã¦ãããã¼ã¿ã使ã£ã¦ãå®éã«æ¤è¨¼ãã¦ã¿ãã
_test_verify :: Either String () _test_verify = do pub <- parsePublicKey "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERYcga9cTuvv0EbOFM0PO/KJjCgqYwtGar22uUyPQPwUbm+OtKXGNGIaHBvkgXBCbTxG4XQ4ddfDPgfMAcguUtg==" verify pub "1655455728" "[{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"processed\",\"category\":[\"cat facts\"],\"sg_event_id\":\"cPeOfuc93o3vAEftXKt_zA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"deferred\",\"category\":[\"cat facts\"],\"sg_event_id\":\"zFYYFn__N8lr3be4TqKnVw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"400 try again later\",\"attempt\":\"5\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"delivered\",\"category\":[\"cat facts\"],\"sg_event_id\":\"rj8WvpmTWgE0z2MSd41KKg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"250 OK\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"open\",\"category\":[\"cat facts\"],\"sg_event_id\":\"uz1AaVpvYihASovU-M-Jrg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"click\",\"category\":[\"cat facts\"],\"sg_event_id\":\"O2bZwfxc-xm-9zmeVZX8HA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"bounce\",\"category\":[\"cat facts\"],\"sg_event_id\":\"vMLN27M1-TVv5XADvpW3Nw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"500 unknown recipient\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"dropped\",\"category\":[\"cat facts\"],\"sg_event_id\":\"u1u9xdYlTkoDkUZFZ_j_tg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"Bounced Address\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"spamreport\",\"category\":[\"cat facts\"],\"sg_event_id\":\"CjkStOi15hPQfWO58rH9dw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"QrCQiwv2pfkejKE5Zi1D0g==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"l67dvY6MIupGpuk1r9vIXw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_resubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"fo7PLpVCoCA9WDHovGQyyw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10}]\r\n" "MEUCIQCBYJiC1zzZeM61EbekWSGMFgpRSzaQSA4zwV3vlMgf/wIgSrMZIIYTnx4dkqDK92re4WYhcM3xEKbLIKfmcu7Et0o=" _test_verify_fail :: Either String () _test_verify_fail = do pub <- parsePublicKey "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERYcga9cTuvv0EbOFM0PO/KJjCgqYwtGar22uUyPQPwUbm+OtKXGNGIaHBvkgXBCbTxG4XQ4ddfDPgfMAcguUtg==" verify pub "1655455729" "[{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"processed\",\"category\":[\"cat facts\"],\"sg_event_id\":\"cPeOfuc93o3vAEftXKt_zA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"deferred\",\"category\":[\"cat facts\"],\"sg_event_id\":\"zFYYFn__N8lr3be4TqKnVw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"400 try again later\",\"attempt\":\"5\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"delivered\",\"category\":[\"cat facts\"],\"sg_event_id\":\"rj8WvpmTWgE0z2MSd41KKg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"250 OK\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"open\",\"category\":[\"cat facts\"],\"sg_event_id\":\"uz1AaVpvYihASovU-M-Jrg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"click\",\"category\":[\"cat facts\"],\"sg_event_id\":\"O2bZwfxc-xm-9zmeVZX8HA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"bounce\",\"category\":[\"cat facts\"],\"sg_event_id\":\"vMLN27M1-TVv5XADvpW3Nw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"500 unknown recipient\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"dropped\",\"category\":[\"cat facts\"],\"sg_event_id\":\"u1u9xdYlTkoDkUZFZ_j_tg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"Bounced Address\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"spamreport\",\"category\":[\"cat facts\"],\"sg_event_id\":\"CjkStOi15hPQfWO58rH9dw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"QrCQiwv2pfkejKE5Zi1D0g==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"l67dvY6MIupGpuk1r9vIXw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_resubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"fo7PLpVCoCA9WDHovGQyyw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10}]\r\n" "MEUCIQCBYJiC1zzZeM61EbekWSGMFgpRSzaQSA4zwV3vlMgf/wIgSrMZIIYTnx4dkqDK92re4WYhcM3xEKbLIKfmcu7Et0o="
ghci> _test_verify Right () ghci> _test_verify_fail Left "Verification failed"
SendGridããéããã¦ãããã¼ã¿ã¯éããæ¹ãããããã¼ã¿ã¯å¼¾ãããã¨ã確èªã§ããã
ã¾ã¨ã
ã¤ã³ã¿ã¼ããããµãã³ãã®ä½äººã¨ãã¦ãä¸æ£ãªæ å ±ã«å¯¾ããè¦æã¯æ ã£ã¦ã¯ãããªããSendGridã¯ããªã¯ã¨ã¹ãã®å 容ã¨ã¿ã¤ã ã¹ã¿ã³ããECDSAã§ç½²åãããã¨ããã·ã³ãã«ãªä»çµã¿ããããããã§ãæ¯è¼çå°ãªãã³ã¼ãã§æ¤è¨¼æ©è½ãå®è£ ã§ããã
PRæ
WebHookã®éã¯ECDSAã«ãã£ã¦éãããã¦ããããæ¡ç¨ã«ããã¦HERPã®éã¯éããã¦ãããHERPã¯é ãè¯ãã¦ç©äºãæãéãããã人ãåéãã¦ãã¾ãã
ä¾åé¢ä¿ã¨é層æ§é ã®è»
21ä¸ç´ç¾å¨ã®ããã°ã©ãã³ã°è¨èªã«ããã¦ãã¢ã¸ã¥ã¼ã«ã¨ããæ©è½ã¯ã»ã¼å¿ è¦ä¸å¯æ¬ ã§ãããã½ã¼ã¹ã³ã¼ããåå²ããæ示çãªä¾åé¢ä¿ãæå®ããä»çµã¿ã§ããã¢ã¸ã¥ã¼ã«ã¯ã以ä¸ã®ãããªæ§ã ãªæ©æµãããããã
- ã¤ã³ã¯ãªã¡ã³ã¿ã«ãã«ã: ã¢ã¸ã¥ã¼ã«ãã¨ã«ã³ã³ãã¤ã«çµæãä¿åããå¤æ´ããã¦ããªãé¨åãåã³ã³ãã¤ã«ããã®ãé²ãã
- å é¨ã®é è½: å®è£ ã®è©³ç´°ãé è½ããã¢ã¸ã¥ã¼ã«å¤ãã触ããªãããã«ãããããã°ã©ã ã®è¦éããããããããä¸æ£ãªæä½ãããæ©ä¼ãæ¸ããã
- ã¢ã¼ããã¯ãã£ã®æ´ç: ã¢ã¸ã¥ã¼ã«ã¯ä»ã®ã¢ã¸ã¥ã¼ã«ãåç §ã§ããããååã¨ãã¦ç¸äºåç §ã¯ããªããä¾åã®åããå®ãããã¨ã§ãé©åãªæ½è±¡åã¨ã ããã«åºã¥ããå®è£ ã®åé¢ãä¿ãã
ãã¦ããããã¢ã¸ã¥ã¼ã«ã便å©ã¨è¨ãã©ãæ°ãå¢ããããã¨ãããã«æ±ãã«ãã*1ããã®ããããã£ã¬ã¯ããªã®æ§é ãã¢ã¸ã¥ã¼ã«ã®é層æ§é ã¨ãã¦éç¨ããä»çµã¿ãåãã£ã¦ãããã¨ãå¤ãã ã³ã³ãã¤ã©ããè¦ãã°ãããããªã©ã§åºåãããã¢ã¸ã¥ã¼ã«åããã対å¿ãããã¡ã¤ã«ãæ¢ãã ãã®è©±ã ãããã¾ã§äººéã®é½åã§ãããä¸ã§æãããããªæ©æµã¨ã¯ãã¾ãé¢ä¿ãã¦ããªãââã ããæ¬å½ã«ããã§ããã®ã ãããã
è¥å¹²ããã¨ãããä¾ã§ã¯ããããæ²ç¤ºæ¿ã®ã¢ããªã±ã¼ã·ã§ã³ãå®è£ ããã«å½ããã以ä¸ã®ããã«ã¢ã¸ã¥ã¼ã«ãåå²ããã¨ãããã
Utilsã¨Modelã®äºã¤ã®ãã£ã¬ã¯ããªãè¨ãããModel.Commentã§ã¯ãæ稿ã表ããã¼ã¿åãããã¼ã¿ãã¼ã¹ã®æä½ãå®ç¾©ãããUtils.Miscã«ã¯éå¤ãªä¾¿å©é¢æ°ãã¾ã¨ããUtils.Markupã§ã¯ãæ稿ãHTMLã«å¤æããå¦çãè¨è¿°ããã
ããããã¾ãããããªãããã®æ§é ã«ã¯åé¡ããããã¨ããã®ããUtilsã¨Modelsã¯ããã£ã¬ã¯ããªåä½ã§è¦ãã¨ãåæ¹åã«ä¾åé¢ä¿ãæã£ã¦ããã®ã ã å è¿°ããéããã³ã³ãã¤ã©ããè¦ãã°ãã¢ã¸ã¥ã¼ã«ãã©ãã®ãã£ã¬ã¯ããªã«å±ãã¦ããããæ¬è³ªã¯å¤ãããªãããæ£ããå ¥åã§ãããããããéçºè ã¯ãã£ã¬ã¯ããªã®åå²ã«æå³ãè¦åºãã¦ããã¨ããé½é½¬ãçºçãã(ããã¦ãã³ã³ãã¤ã©ã¯ãããææãããããªã)ããã®é½é½¬ãæ¾ç½®ããã¾ã¾éçºãç¶ãã¦ããã¨ãæ³å®å¤ã«æ·±ãä¾åé¢ä¿ãçºçãã¦ãã«ããé ããªã£ããããModelã«ä¾åããUtilsãã¨ããåå¨ã¨è¾»è¤ãåãããããã«ãæ¬æ¥é è½ãã¹ã詳細ãã©ãã©ãæ¼ãåºããããã¦ãã¾ãã
ãããé²ãæ段ã¨ãã¦ãããããã®ãã£ã¬ã¯ããªãããã±ã¼ã¸åããä¾åé¢ä¿ãæ示ããã¨ãããã®ããããããã¯åæ¹åã®ä¾åãé²ãã¨ããæå³ã§ã¯å¹æçãªä¸æ¹ãããã±ã¼ã¸ãã¨ã®ã¡ã¿ãã¼ã¿ã管çããã³ã¹ããé«ã¾ã£ãããããã±ã¼ã¸åä½ã§ã®ãã£ãã·ã¥ã¯ã§ãã¦ããã¢ã¸ã¥ã¼ã«åä½ã®ã¤ã³ã¯ãªã¡ã³ã¿ã«ãã«ããå¹ãã«ãããªããªã©ããã¡ãªãããç¡è¦ã§ããªãã
ãªãã°ãæ°ãã«éç解æãå®è£ ãããããã£ãåé¡ãæ¤åºãããã§ã¯ãªãããã¾ãã¯ã¢ã¸ã¥ã¼ã«ã®ä¾åé¢ä¿ã®ã°ã©ããå¯è¦åãã¦è¦éããç«ã¦ããââdotå½¢å¼ã«å¤æããgraphvizã§è¡¨ç¤ºããã ãã®ç°¡åãªè©±ã ã
â¦â¦ã©ãããããããªç°¡åãªè©±ã§ã¯ãªãã£ãããã ã
ããå°ãããã£ã¬ã¯ããªå士ã®ä¾åé¢ä¿ã«çç®ãã¦èãã¦ã¿ãããFoo.Bar.A.BãFoo.Baz.C.Dã«ä¾åãã¦ããã¨ãããBarãBazã«ä¾åãã¦ãããã¨ããæ å ±ããæ®ãã°ãããããæ·±ãé¨åã®æ å ±ã¯åãã§ãæ§ããªããã¤ã¾ãã以ä¸ã®ãããªã¢ã«ã´ãªãºã ã§ã°ã©ããå°ãåºãã
- ä¾åå ãä¾åå ã®ã¢ã¸ã¥ã¼ã«åããããã§åºåãããªã¹ãã«ãã
- ã©ã¡ããã®ãªã¹ãã空ã«ãªã£ãããçµäºãã
- ãªã¹ãã®å
é ãè¦ã
- ååãåããªããå é ãåãé¤ã
- ååãéã£ã¦ãããã辺ãä½æãã
ãFoo.BarãFoo.Bar.Bazã«ä¾åãã¦ãããã®ãããªé¢ä¿ã¯ä»åã¯èæ ®ããªããããèæ ®ãããå ´åã¯ãããªã¹ãã空ã«ãªã£ããã辺ãä½æãããããã«æ¸ãæããã°ããã
ãã®ã¢ã«ã´ãªãºã ãå©ç¨ããã¨ã親ã®ãã£ã¬ã¯ããªãã¨ã«åå²ãããã°ã©ããã§ããgraphvizã®circoã使ãã¨å³ã®ããã«ãªããæåã®ã°ã©ãã¨ã¯ãããéãã ã
巨大ãªã³ã¼ããã¼ã¹ã«ãããã¢ã¸ã¥ã¼ã«éã®ä¾åé¢ä¿ã¯ãæç´ã«æç»ããã¨ããã§ã¹ãã²ããã£ã©ããã§ã¯ãªã度ãé£ãã¢ãã«ãããªããªããããã§ãé層ãã¨ã«ä¾åé¢ä¿ããå²ããââããªãã¡ãã¢ã¸ã¥ã¼ã«ãã¾ã¨ãããã©ã«ãå士ã«ãä¾åé¢ä¿ãè¦åºããå帰çã«è¦ç´ãããã¨ã«ãã£ã¦ãç¾ããå¯è¦åã§ãã pic.twitter.com/dwx3MA2DCH
— ãµã¿ a.k.a.DJ Monad (@fumieval) 2022å¹´4æ28æ¥
æåãã¯ãã¼ãºã¢ããããã¨ä»¥ä¸ã®ããã«ãªãããã£ã¬ã¯ããªã«è¦ç´ããã¦ãããããé¢ä¿ãèªã¿ããããªã£ã¦ããã®ããããã
ã ãæ¬é¡ã«æ»ããããã£ã¬ã¯ããªã®æ義ãä¿ã¤ããã«ã¯ãç¸äºä¾åããªãââãããããã«æåéè·¯ãåå¨ããªããã¨ãä¿è¨¼ããããã¤ã¾ããå¼·é£çµæåå解ããã°ããã®ã ãæ©éä¼ç¤¾ã®ã³ã¼ããã¼ã¹ã«é©ç¨ãã¦ã¿ãã¨ãããªãã®å¤§ç©ãé£ãããå辺ã®éã¿ã¯ã¤ã³ãã¼ãã®ä»¶æ°ã表ãã
ãã®ã°ã©ãã¯å¼·é£çµã§ãããããªãã¡ã©ã®é ç¹ã®éã«ãéãåå¨ãããæ¬æ¥ããã®ãããªãã¨ãçºçããªãããã«ããã±ã¼ã¸ãåå²ãã¦ããã¯ããªã®ã§å°ã é©ãã®çµæã ãããã±ã¼ã¸ãç´°ããããããã«ãæãéãã§ãã¦ãã¾ã£ãã®ã ããã
æåéå·¡åã°ã©ãã«ããããã«åãé¤ãã¹ã辺ã¯Feedback arc set(FAS)ã¨å¼ã°ãã¦ããã æå°ã®FASãæ±ããã®ã¯NPå°é£ã ããä»åã®ã±ã¼ã¹ã§ã¯æãéã®éã¿ãå§åçã«å°ããããã®ãããéã辺ããé ã«åæ§ç¯ãã¦ãããéè·¯ãçãåè£ã ãå¼¾ã貪欲æ³ã§å®ç¨çãªçµæãå¾ããããã ã
件ã®ã°ã©ãã«ã¤ãã¦ã¯ã以ä¸ã®åã¤ãé¤å»ããã¨DAGã«ãªãã幸ãã件æ°ã¯ããã»ã©å¤ããªãã®ã§ã循ç°ããªããã®ã¯ããã»ã©é£ãããªãããã ã
Queries -> QueryServices Effects -> JobWorker SendGrid -> Effects DomainObjects -> Effects
ããã¾ã§ã®å¦çãèªååãããããHaskellã³ã³ãã¤ã©ã®ãã©ã°ã¤ã³ã¨ãã¦å®è£
ããã¹ã¿ã³ãã¢ãã³ã®ã³ãã³ãã©ã¤ã³ãã¼ã«ã¨ãã¦å®è£
ããã orderorder src
ãå®è¡ããã¨ãsrc以ä¸ã®ã¢ã¸ã¥ã¼ã«å士ã®ä¾åé¢ä¿ã調ã¹ãå¼·é£çµæåãåæããããã®çµæãä¿åãã¦ããã¦å·®åãCIãªã©ã§ãã§ãã¯ããã°ãé¢ä¿ãç ´å£ãããããªä¾åé¢ä¿ãå¢ããéã«æ°ä»ããã¨ãå¯è½ã«ãªãã
ãã¡ãããããã¾ã§ã®è°è«ã¯Haskell以å¤ã«ãé©ç¨ã§ãããããä»ã®è¨èªã«ãåçã®ãã®ãå®è£ ãããã¨ã¯å¯è½ãªã¯ãã ã
ã¾ã¨ã
ä»ã¾ã§ã¯ã¢ã¸ã¥ã¼ã«ã®é層æ§é ã«æ確ãªã«ã¼ã«ããªããããæå³é£¾ãã ã£ãããããããã£ã¬ã¯ããªéã®ä¾åé¢ä¿ãã¨ããæ¦å¿µãå°å ¥ãããã¨ã§ãèªç¶ã«ãã¢ã¸ã¥ã¼ã«ã®ä¾åé¢ä¿ãDAGã§ããããã«ããã£ã¬ã¯ããªã®ä¾åé¢ä¿ãã¾ãDAGã«ãªããã¨ããå¶ç´ãå°å ¥ã§ããæã ã®ç´è¦³ã¨ã®ããåãããã§ããããã®ä»çµã¿ãã¯ãªã¼ã³ã¢ã¼ããã¯ãã£ã®ä¿é²ã»ä¿å®ã«æ´»ç¨ãã¦ããããã
PRæ
ããã¾ã§æ£ã é層æ§é ã®è©±ããã¦ããããHERPã¯ãªã¼ãã³ã§ãã©ãããªçµç¹ãæ¯ã¨ãã¦ãããç¾å¨ãç©æ¥µçã«æè¡è ãåéãã¦ãããèå³ã®ããæ¹ã¯ã以ä¸ã®è³æãåç §ããããã
*1:ã¯ã¤ã«ãã«ã¼ãã使ãã¨ãã³ãã³ãã©ã¤ã³å¼æ°ã®é·ããéçãè¶ ãã¦ãã¾ããªã©ãç¾å®çãªæ¯éãçºçãã
æ ªå¼ä¼ç¤¾HERPã«è»¢è·ãã¾ãã
GitHubã®ãããã£ã¼ã«ãè¦ã人ãªã©ã¯ããããããæ°ã¥ãã¦ãããããããªãããTsuru Capital LLCãéè·ãã2022å¹´2æ16æ¥ããHERPã®æ£ç¤¾å¡ã¨ãªã£ããHERPã¯ã大ã¾ãã«è¨ãã°æ¡ç¨æ´»åã管çãããã©ãããã©ã¼ã ãæä¾ãã¦ããã
ãã£ãã
Tsuru Capitalã¯Kospi(éå½æ ªã®ã¤ã³ããã¯ã¹)ã®ããªããã£ããåèªååå¼ããä¼æ¥ã§ãHaskellã使ã£ã¦ããã¨ããã®ãæ大ã®ç¹å¾´ã¨ãã¦ç¥ãããè¬å¤ãä¼ç¤¾ã ã2015å¹´ã«å ¥ç¤¾ãã2022å¹´ã¾ã§6年以ä¸åãããæµç³ã«åãè·å ´ã«ãã£ã¨ããã¨çµé¨ãåã£ã¦ãã¾ãããæè¦ã¨ãã¦ã飽ããããã®ã§è»¢è·ãèããã ã¾ããããææããRustã¡ã¤ã³ã®éçºãããããã«ãªã£ããããã¯ãèªåã®æ大ã®å¼·ã¿ã§ããHaskellãæ´»ãããä»äºããããã転è·å ãèããåºæºã¨ãªã£ãã æä½ã®ã©ã¤ãã©ãªã§ããextensibleã使ã£ã¦ããã¨ããæ å ±ããã£ããããåµå¯ãã¦ãHERPã«å¿åããã
å ¥ç¤¾ã¾ã§ã®æµã
æ¡ç¨ããã»ã¹ãæ±ãä¼æ¥ãªã ããã£ã¦ããªãè¿ éã§ãä½é¨ãè¯ãã£ãã
- 2021/12/14: ã¦ã§ããµã¤ãããå¿å
- 2021/12/15: ã«ã¸ã¥ã¢ã«é¢è«
- 2021/12/16: ä¸æ¬¡é¢æ¥(æ§é åé¢æ¥ãäºåã®ã¢ã³ã±ã¼ãã§çããå 容ããã¨ã«ãåè·ã®ææãªã©ã«ã¤ãã¦è©±ãã)
- 2021/12/21: äºæ¬¡é¢æ¥(価å¤è¦³ãé©åãããã©ããããã«ã¸ã¥ã¢ã«ãªä¼è©±ã§ç¢ºèªãã)
- 2021/12/28: ãªãã¡ã¼é¢è«
並è¡ãã¦ãªãã¡ã¬ã³ã¹ãã§ãã¯ãè¡ã£ã(ä¸å¸ãååãé¨ä¸1åãã¤ãå¸æãããããåºæ¬çã«é層æ§é ããªãçµç¹ãªã®ã§èªåã®è£éã§3人é¸ãã )ã
ãã¾ãã«ãã¹ã ã¼ãºã¨ãããããã¡ããä¸æ¹çã«æå©ãªããã«æããããã®ã§ãããå°ã試ãã¦æ¬²ããã£ãæ°ããããã ããHERPã®æåã¨èªç¤¾ã®è£½åããªãåè£è ä½é¨ã¯ç¢ºå®ã«å¼·ã¿ã¨è¨ããã
ç¾å¨
ã³ã¼ããã¼ã¹ã®å質ãæ¹åããããæ°æ©è½ãå®è£ ããããæ´ãç©ããããã¨èªä¸»çã«è²ã ãªãã¨ããã£ã¦ãããèªåã®ã¹ãã«ã«ã¤ãã¦æ¥µç«¯ãªä¸è¶³ãéå°ãæãããã¨ã¯ãªããã³ãã¥ãã±ã¼ã·ã§ã³ãçããªãããé¢ç½ã課é¡ã¯å°½ããã楽ãããã£ã¦ããããã ã¨æããéçºè ãã¼ã ã¯é©åº¦ã«ãªã¿ã¯æ§ã¨ç¤¾ä¼æ§ãããããã©ãã©ãããã¦ããªãã¨ããã好æãæã¦ãã
ããªãé«ãæå¾ ãåãã¦å ¥ç¤¾ããã®ã§ãããã«å¿ããããã«ææãåºãã¦ããããã
ãªã³ã¯
Oath: å®å ¨ãé«éãåæå¯è½ãªä¸¦è¡å¦ç
TL;DR
並è¡å¦çãç°¡æ½ãã¤å®å
¨ã«è¨è¿°ã§ããã©ã¤ãã©ãªãä½ã£ããApplicativeDoæ¡å¼µã使ã£ã¦ã以ä¸ã®ããã«oath
ã®å¼æ°ã¨ãã¦ä¸ããIOã¢ã¯ã·ã§ã³ãåæã«å®è¡ããçµæãéããå¦çãæ¸ããããããããä¾å¤ãæããå ´åãæ®ãããã£ã³ã»ã«ãããããªã½ã¼ã¹ãæ¼ããå¿é
ããªãã
evalOath $ do a <- oath $ ... b <- oath $ ... f a b
çµç·¯
Haskellã¯ä¸¦è¡å¦çãå¾æã¨ããã¦ãããäºå®ã軽éã¹ã¬ãããMVarãSTMã¨ãã£ãå¦çç³»ã«ãããµãã¼ããå å®ãã¦ãããHackageã®Concurrencyã«ãã´ãªã«ã¯235ãã®ããã±ã¼ã¸ããããã¨ãããã¦ã¼ã¶ã®é¢å¿ã®é«ãã窺ããã 並è¡å¦çãå®ç¨ããä¸ã§ã¯ãåã«ã¹ã¬ããããã©ã¼ã¯ããã ãã§ãªããè¨ç®ã®çµæãéããå¿ è¦ãããââScalaãªã©ã§è¨ãFutureã«è¿ããã®ãããã¨å¬ãããæ¡ã®å®ã並è¡è¨ç®ã®çµæãåãåºãããã®Haskellã©ã¤ãã©ãªã¯æ°å¤ãä½ããã¦ããã
futuresã¨ãããªããªãããååã®ããã±ã¼ã¸ããããAPIãããªãã·ã³ãã«ã ã ã¹ã¬ããããã©ã¼ã¯ãã¦ãè¨ç®çµæãMVarã«ä»£å ¥ããMVarã®ä¸èº«ãåãåºãã¢ã¯ã·ã§ã³ãè¿ãã¨ãããã®ã ã
fork :: IO a -> IO (Future a) fork io = do mv <- newEmptyMVar forkIO $ do a <- io putMVar mv a return $ Future $ readMVar mv
èãæ¹ã¯ãããããããããã®å®è£
ã«ã¯è´å½çãªæ¬ ç¹ãããââä¾å¤å¦çã§ãããforkã«ä¸ããã¢ã¯ã·ã§ã³ãä¾å¤ãçºçããã¦ãã誰ã対å¿ãã§ããªããããããputMVar
ãå¼ã°ããªããªãã®ã§readMVar
ãçºæ£ãã¦ãã¾ã(å®è¡æã¨ã©ã¼ã«ãªã)ãããã§ã¯å®ç¨ããã®ã¯é£ãããã¾ããforkIOã®çµæã§ããThreadIdãæ¨ã¦ããã¦ãããã¨ãããããããã«ãä¸åº¦å§ããè¨ç®ãå¤é¨ããæ¢ãããã¹ã¯ãªãã
spawnã¯ãFutureåã§ã¯ãªãç´æ¥IOãè¿ãç¹ãé¤ãã°futuresã¨ä¼¼ã¦ããã
spawnTry :: IO a -> IO (IO (Result a)) spawnTry m = do v <- newEmptyMVar _ <- mask $ \restore -> forkIO (try (restore m) >>= putMVar v) return (readMVar v) spawn :: IO a -> IO (IO a) spawn m = do r <- spawnTry m return (r >>= either throwIO return)
ãã¡ãã¯ãã¡ãã¨ä¾å¤å¦çããã¦ãããputMVarãå¼ã°ãããã¨ãä¿è¨¼ãããããããããå®ç¨çã ããããããã¯ãè¨ç®ã¯æ¢ããããªãã
promiseã¯ãApplicativeã¨Monadã®ã¤ã³ã¹ã¿ã³ã¹ã§ããPromiseåã売ãã ã
newtype Promise a = Promise { unPromise :: IO (Async a) } deriving (Functor) instance Applicative Promise where pure = Promise . async . return Promise mf <*> Promise mx = Promise $ do f <- mf x <- mx (f', x') <- waitBoth f x async $ return $ f' x' instance Monad Promise where return = liftIO . return Promise m >>= f = Promise $ m >>= wait >>= unPromise . f liftIO :: IO a -> Promise a liftIO = Promise . async
å¤å´ã®IOã§ã¹ã¬ããããã©ã¼ã¯ããå
å´ã®Asyncã§çµæãå¾
ã¤ã¨ããä»çµã¿ã®ããã ãã ããm <*> n
ã¯mã¨nããããã並è¡ãã¦å®è¡ããã®ã¡ã両æ¹ã®çµæãè¿ã£ã¦ããã®ããã®å ´ã§å¾
ã¤å¿
è¦ããã(ãå¾
ã¤Asyncãè¿ããã®ã§ã¯ãªãããå¾
ã£ã¦ããAsyncãè¿ãã)ãããã§ã¯(a *> b) *> c
ã¨a *> (b *> c)
ã®æåãç°ãªã£ã¦ãã¾ããããApplicativeã®åãæºãããªããã¾ããAsyncã¯æ¬æ¥cancel`ã§ããã¯ãã ããåæä¸ã«çµæãå¾
ã£ã¦ãã¾ãã®ã§äºå®ä¸ä¸æãã§ããªã(ã©ã¡ãã«ãããPromiseã®å®è£
ãé è½ããã¦ããã®ã§ã©ããããããªãã)ã
ããã«ãMonadã¯å·¦ã®è¨ç®ã®çµæãå¾
ã£ã¦ããå³ã«é²ãã¨ããæåã§ãApplicativeã¨ã®ä¸è²«æ§ããªãããããã¤ã³ã¹ã¿ã³ã¹ããã£ã¦ããããã§ããã°ã©ã ãçµã¿ç«ã¦ãã®ã¯é£ããã ããã
éåæå¦çã®å®çªã§ããasyncããã±ã¼ã¸ã«ã¯ãConcurrently
ã¨ããåãããã
newtype Concurrently a = Concurrently { runConcurrently :: IO a } instance Applicative Concurrently where pure = Concurrently . return Concurrently fs <*> Concurrently as = Concurrently $ (\(f, a) -> f a) <$> concurrently fs as instance Alternative Concurrently where empty = Concurrently $ forever (threadDelay maxBound) Concurrently as <|> Concurrently bs = Concurrently $ either id id <$> race as bs
(<*>)
ã¯concurrently :: IO a -> IO b -> IO (a, b)
ã使ã£ã¦ä¸¡æ¹ã®çµæãå¾
ã¤è¨ç®ã表ç¾ãã(<|>)
ã¯race :: IO a -> IO b -> IO (Either a b)
ã¯ã©ã¡ãããè¿ã£ã¦ããã¾ã§å¾
ã¤è¨ç®ã表ç¾ãããpromise
ã¨éãããã®å ´ã§å¾
æ©ããå¿
è¦ããªãã®ã§ãApplicativeåãæºããããã ã
concurrently
ããã³race
ã¯ãã¹ã¬ãããå¿
ãäºã¤ãã©ã¼ã¯ããä¸ãããªãè¤éãªå®è£
ãªã®ã§ããªã¼ãã¼ãããã大ãããã ãããã¾ã§è¦ãä¸ã§ã¯ä¸çªæ£ãããã§ä½¿ããããããªå®è£
ã ãããã£ã¨ããæ¹æ³ã¯ãªãã ãããã
ç¶ç¶ã¨STMã®ã³ã³ã
éåæå¦çãå®å ¨ã«åæããæ¹æ³ã«ã¯è¦ããããã単純で頑強なメッセージングシステム、franz - モナドとわたしとコモナドã§ç´¹ä»ãããç¶ç¶æ¸¡ããç¨ãã¦çµæãå¾ ã¤ãã©ã³ã¶ã¯ã·ã§ã³ã渡ãã¨ããä»çµã¿ã ããã®æ¹æ³ãªããç¶ç¶ãçµäºããã¿ã¤ãã³ã°ã§å¦çãä¸æã§ãããããå¾ ã¤ãããããèªç±èªå¨ããªã½ã¼ã¹ã®è§£æ¾æ¼ãã®å¿é ã¯ãªã(franzã¯ãã®ã¡ã«ããºã ã«ãã£ã¦éåæãªã¯ã¨ã¹ãã管çãã¦ãã)ããã®ã¨ãã»ã³ã¹ãç¬ç«ããã©ã¤ãã©ãªã«è¸çã§ãããã ãããã£ã½ãååã¯ã ãããåããã¦ããã®ã§ãPromiseããã®é£æ³ã§Oathã¨åä»ããã
newtype Oath a = Oath { runOath :: forall r. (STM a -> IO r) -> IO r } deriving Functor instance Applicative Oath where pure a = Oath $ \cont -> cont (pure a) Oath m <*> Oath n = Oath $ \cont -> m $ \f -> n $ \x -> cont (f <*> x)
Oath
ã¯ãIO (STM a)
ãCPSå¤æãããã®ã§ãCompose (Codensity IO) STM
ã¨ç価ã§ãã*1 *2ãå¤å´ã®IO
ã§è¨ç®ãèµ·åããå
å´ã®STM
ã§çµæãå¾
ã¤ãOath
ã¯Applicative
ã¤ã³ã¹ã¿ã³ã¹ãæã¡ãSTM (a -> b)
ã¨STM a
ãç¨æãã¦ãããããããåæããSTM b
ãè¿ãââè¨ç®ã®èµ·åã¨ãçµæã®å¾
æ©ãå¥ã
ã«åæããã¨ããããã ã
oath :: IO a -> Oath a oath act = Oath $ \cont -> do v <- newEmptyTMVarIO tid <- forkFinally act (atomically . putTMVar v) let await = readTMVar v >>= either throwSTM pure cont await `finally` killThread tid
oath
ã¯ãIOã¢ã¯ã·ã§ã³ãå¥ã®ã¹ã¬ããã§å®è¡ãããã®çµæãTMVarçµç±ã§åãåºããforkFinally
ãä¾å¤ãåãæ¢ããthrowSTM
ããããä¼ããã
ç殺ä¸å¥ªã®æ¨©ã¯ç¶ç¶ãæ¡ã£ã¦ãããçµäºããã¨ãã«ThreadKilled
ä¾å¤ãæããããããã®ãããªæåãwithAsyncãªã©ã§å®å
¨ã«è¡¨ç¾ãããã¨ããã¨ãwithAsync foo $ \a -> withAsync bar $ \b -> ...
ã®ããã«
ãã¹ããæ·±ããªã£ã¦ãã¾ããã¡ã ããOathã¯ç¶ç¶æ¸¡ãã®æ½è±¡åã«ãã£ã¦ãcombine <$> oathSTM foo <*> oathSTM bar
ã®ããã«ãã©ããã«æ¸ãããããã ãã§ãªããtraverse
ã§ã³ã³ããã«å¯¾ãã¦ãç°¡åã«é©ç¨ã§ããã¨ããå©ç¹ãããã
Oath
ãå®è¡ããã«ã¯ãåã«evalOath
ãå¼ã³åºãããã¡ãããçµæãå¾
ã¤ã¿ã¤ãã³ã°ãå¶å¾¡ãããå ´åã¯runOath
ãç´æ¥å¼ã¶ã¹ãå ´é¢ãããã ããã
evalOath :: Oath a -> IO a evalOath m = runOath m atomically
Alternative
ã®<|>
ã¯ãConcurrently
ã¨åæ§ä¸¡æ¹ã®è¨ç®ãèµ·åããããä¸æ¹ãå®äºãã段éã§ããçæ¹ã¯ãã£ã³ã»ã«ãã(STMã®Alternativeã¤ã³ã¹ã¿ã³ã¹ãç¶æ¿ãã¦ãã)ã
Control.Concurrent.STM.Delayã®ã©ããã¼ãæä¾ãã¦ããã<|>
ã§åæããã ãã§ãã¿ã¤ã ã¢ã¦ãå¦çãé常ã«ç°¡åã«è¨è¿°ã§ããã
ã¾ããããªã½ã¼ã¹ã®ç¢ºä¿ãã解æ¾ãã使ç¨ãã¨ããä¸ã¤ã®æåãä¸ã¤ã«ã¾ã¨ããããã¨ããç¶ç¶æ¸¡ãã®å¼·ã¿ãdelay
ã®å®è£
ã«ãã表ãã¦ããã
instance Alternative Oath where empty = Oath $ \cont -> cont empty Oath m <|> Oath n = Oath $ \cont -> m $ \a -> n $ \b -> cont (a <|> b) delay :: Int -> Oath () delay dur = Oath $ \cont -> bracket (newDelay dur) cancelDelay (cont . waitDelay)
Concurrently
ã¨éããOath
ã¯ãã©ã¼ã¯ã¯å¿
é ã§ã¯ãªãã®ããã¤ã³ãã ãä¾ãã°ãããã¯ã¼ã¯çµç±ã§ãªã¯ã¨ã¹ããéä¿¡ããå¦çã¯æ¸ããé åºã§å®è¡ããçµæãå¾
ã¤é¨åã ãéåæã«ããã¨ãã£ãå®è£
ãã§ãããforkOn
ãªã©ãforkIO
以å¤ã®ãã©ã¼ã¯ã使ããããèªç±åº¦ãé«ãããããããã©ã¼ã¯ããªãã¨ããé¸æè¢ãããããã決å®æ§ãæ±ããããåä½ãã¹ãã®å®è£
ãªã©ã«ãå½¹ã«ç«ã¤ã ããã
ããã©ã¼ãã³ã¹
æå¾ã«ãConcurrently
ã¨æ¯è¼ããããã®ãã³ããã¼ã¯ãè¡ã£ããä»ã®ããã±ã¼ã¸ã¯ã¾ã¨ãã«åä½ããªããããè©ä¾¡ã®å¯¾è±¡ããå¤ãã(åä½ãã¹ãã¯æ®ãã¦ãã)ã
, bench "oath STM 100" $ nfIO $ O.evalOath $ traverse (O.oath . pure) [0 :: Int ..99] , bench "async 100" $ nfIO $ A.runConcurrently $ traverse (A.Concurrently . pure) [0 :: Int ..99]
oath IO 10: OK (0.86s) 3.18 μs ± 206 ns oath STM 10: OK (0.34s) 5.10 μs ± 169 ns async 10: OK (0.66s) 10.0 μs ± 190 ns oath IO 100: OK (0.60s) 35.8 μs ± 2.4 μs oath STM 100: OK (0.39s) 48.0 μs ± 1.3 μs async 100: OK (0.21s) 100 μs ± 5.6 μs
ãªã¼ãã¼ãããã¯Concurrently
ã®ç´ååã«æãããã¦ãããConcurrentlyã¯(<*>)
ã®å·¦å³ãã¨ã«ã¹ã¬ããããã©ã¼ã¯ãããããé
ã®äºåãã©ã¼ã¯ããå¿
è¦ãããã¨ããç¹ã表ãã¦ããã®ã ããã
ã¾ã¨ã
Oath
ã¯ãç¶ç¶æ¸¡ãã¹ã¿ã¤ã«ã¨STMãçµã¿åããããã¨ã«ãã£ã¦ãæè»ãå®å
¨ãé«éãããã¦åæå¯è½ãªéåæå¦çã®è¡¨ç¾ãå¯è½ã«ãããåã«IOã¢ã¯ã·ã§ã³ãçµã¿ç«ã¦ãéå
·ã¨ãã¦ã便å©ã ããä»å¾ã¯Oathã«åºã¥ããAPIãã¶ã¤ã³ã«ãä¸èã®ä½å°ãããã¨èãã¦ããã並è¡å¦çã®æ°å®çªãçã£ã¦ããããã
æ°ããGHCæ¡å¼µãNoFieldSelectorsã«ã¤ãã¦
ä»ã¾ã§ä¸æºã®å¤ãã£ãHaskellã®ã¬ã³ã¼ãã®æ±ããæ¹åããããã®ä¸æ©ã¨ãã¦ãNoFieldSelectorsã¨ããGHCæ¡å¼µã®å®è£ ãé²ãã¦ããã
åæ©
Haskellã«ã¯ã¬ã³ã¼ããå®ç¾©ããããã®æ§æãããã
data User = User { userId :: Int , userName :: Text }
ããå®ç¾©ããã¨ãåãã£ã¼ã«ããã¨ã«userId :: User -> Int
ã¨userName :: User -> Text
ã¨ããã²ãã¿ã¼ã«ç¸å½ããé¢æ°ãçæãããããããã®é¢æ°ã¯ç¹å¥ãªæå³åããæã£ã¦ããã以ä¸ã®ã¬ã³ã¼ãæä½ã®æ§æã«ãå©ç¨ã§ããã
- æ§ç¯
User { userId = 0, userName = "Zero" }
- ãã¿ã¼ã³ããã
case foo of User { userId = x, userName = name } -> ...
- æ´æ°
foo { userId = 1 }
ãããããã£ã¼ã«ãã¨åãååã®é¢æ°ããã使ãã¥ããã®åå ã¨ãªã£ã¦ããã
å¤ãã®ããã°ã©ãã³ã°è¨èªã§ã¯ãæ§é ä½ã®ãã£ã¼ã«ãã¯ããããåºæã®åå空éã«å±ããããHaskellã¯ããã§ã¯ãªãã以ä¸ã®ãããªå®ç¾©ã¯ãid
ãUser
ãªã®ãArticle
ãªã®ããããã¨ãPrelude
ã®id :: a -> a
ãªã®ãå¤å¥ã§ããªããããå®éã«ã¯ä½¿ããªãã
data User = User { id :: Int, name :: Text } data Article = Article { id :: Int, title :: Text }
ãã®ãããuserId
ãarticleId
ã®ããã«ååãæ¥é è¾ã«ããã®ãéä¾ã¨ãªã£ã¦ããã
ããã¨ã¿ã¤ãæ°ãå¢ããã°ããããJSONãªã©ã®ãã©ã¼ãããã«å¤æããéã«æ¥é è¾ãåãåãããã®ä»çµã¿ãªã©ãå¿
è¦ã«ãªãã使ãåæããããªããlensãæ¡å¼µå¯è½ã¬ã³ã¼ããªã©ã®ææ³ã§æ¹åã§ããé¢ããããã®ã®ãRecordWildCards
ãNamedFieldPuns
ãªã©ã®æ§æçãªæ¯æ´ãã網ç¾
æ§ã®ãã§ãã¯ãåããããªããªãã®ã¯çãã
DuplicateRecordFieldsæ¡å¼µãç¨ããã°ãè¤æ°ã®ãã¼ã¿åã§åããã£ã¼ã«ãåãæ¡ç¨ãããã¨ãä¸å¿è¨±ããããããããã²ãã¿ã¼é¢æ°ãã©ã®ãã¼ã¿åã«å±ãããå¤å®ããããã®ãã¸ã«ã«ãªå®è£ ããããé²ãã§ä½¿ããããã®ã§ã¯ãªãã®ãå®æ ã§ãã(ãã®ããã¯ãç¡ããææ¡ãæè¿åçããã *1 )ã
å¿ããã¡ã ããè¤æ°ã®ã³ã³ã¹ãã©ã¯ã¿ãæã¤ãã¼ã¿åã«ããã¦ãã¬ã³ã¼ãã®æ§æã¯ä½¿ããããã®å ´åã対å¿ããã³ã³ã¹ãã©ã¯ã¿ä»¥å¤ã«ã¯ã¨ã©ã¼ãåºãé¨åé¢æ°ãçæããããã大å¤ä½¿ãåæãæªããå®è³ªçã«ãªããã®ã¨ãã¦æ±ããã¦ããã
ææ¡
ãããªåé¡ã解決ããã¢ããã¼ãã¨ãã¦ãNoFieldSelectors
ã¨ããæ¡å¼µãææ¡ãããã
ãã®æ¡å¼µãæå¹ã«ããã¨ãã¬ã³ã¼ããå®ç¾©ãã¦ããã²ãã¿ã¼é¢æ°ã¨ãã¦ã¯ä½¿ããªããªãããã¬ã³ã¼ãã®æ§æã¨ãã¦ã¯ä½¿ããââã¤ã¾ããçãååã®ãã¡ãªãã(ã³ã³ããªã¯ã)ããªãããã¡ãªãã(ç°¡æ½ãªã³ã¼ã)ã ããå¾ãããã¨ããããã ãDuplicateRecordFields
ã¨ä½µç¨ããã°ãè¤æ°ã®ãã¼ã¿åã§åããã£ã¼ã«ãåãæ°å
¼ããªãå®ç¾©ã§ããã
ç®ã®ä¸ã®ãããã¶ã ã£ãã²ãã¿ã¼é¢æ°ã®åé¡ãåé¿ã§ããã°ãä»ã®è¨èªã¨éè²ãªãã¬ã³ã¼ãæä½ãå¯è½ã«ãªããã¬ã³ã¼ãèªä½ã®æ¡ç¨çãé«ã¾ããã¨ãäºæ³ãããã ãã¬ã¼ã³ãªãã¼ã¿åã§èµ·ããã¡ãªãå¤æ´ã«ä¼´ãç ´å£ããå¤ã®é çªãééãããã°ãåé¿ãããããªããHaskellã®å¼·ã¿ã®ä¸ã¤ã§ããã¸ã§ããªã¯ã¹ã使ã£ãå°åºæ©æ§ãæ´»ç¨ã§ããå ´é¢ãå¢ããã
ããã«ããããå®è£ ãããã§ãããRecordDotSyntax(ãããã¼ã¶ã«, æ¥æ¬èªã®ç´¹ä»ã¹ã©ã¤ã)ã®å®ç¨æ§ãé£èºçã«é«ããã ããã
è¦ç´ããã¨ãNoFieldSelectors
ã¯ä»¥ä¸ã®ã¡ãªãããããããã
- ãã£ã¼ã«ãåã«æ¥é è¾ãä»ããªãã¦ãããªã
instance FromJSON Foo
ã®ããã«ããã®ã¾ã¾ã¤ã³ã¹ã¿ã³ã¹ãå°åºã§ããå ´é¢ãå¢ãã- ä»ã¾ã§ã¯åãªãããããã©ã¯ãã£ã¹ã ã£ããè¤æ°ã³ã³ã¹ãã©ã¯ã¿ã®ã¬ã³ã¼ããå®ç¨çã«ãªã
- 害ãåã¼ãå¿é ãªãã¬ã³ã¼ããå°å ¥ã§ãã
å®è£
ææ¡è ã®Simon Hafneræ°ã«ããããã©ã°ã®è¿½å ã試é¨çãªå®è£ ãä½ãããç§ãå®éã«æ©è½ãã段éã¾ã§ããããå®æããããç¾å¨ã¯ã¬ãã¥ã¼æ®µéã«ããã
Implement NoFieldSelectors (!4017) · Merge Requests · Glasgow Haskell Compiler / GHC · GitLab
åã«é¢æ°ã®çæããããã°ãããã¨æããããããã¾ã§åç´ãªè©±ã§ã¯ãªãã£ãããã ãå種ã¬ã³ã¼ãæä½ãã³ã³ãã¤ã«ããã¨ãã®æ¯ãèãã¯ãã²ãã¿ã¼é¢æ°ã®åå¨ãåæã¨ãã¦ããããããçãã¦ãã¾ãã¨ãåãªãã¬ã³ã¼ãã§ãªããã¼ã¿åã¨åãã«ãªã£ã¦ãã¾ãã®ã ããã£ã¼ã«ãã«ç¸å½ããã·ã³ãã«ã¯ä»ã¾ã§éãä½ãããããé ã¨ãã¦ã³ã³ãã¤ã«ããã¨ãã¯ãããé ããã¨ããã¢ããã¼ããã¨ã£ãããã®è¾ºãã¯ãDuplicateRecordFieldsãªã©ã®æ©è½ã¨ã®å ¼ãåãã§æ³¥èããã®ã¨ãªã£ãããAdam Gundryæ°ã®å©è¨ã®ãããã§å®è£ ã¾ã§æã£ã¦ãããã¨ãã§ããã
ãã®ãã©ã³ãã¯GHC 9.2ã¾ã§ã«ã¯ãã¼ã¸ã§ããããã«ããããNoFieldSelectorsã¯ãä»ã¾ã§é¿ãããã®ãªãã£ãæ £ç¿ãè¦ãæ©è½ã§ããããããåºã¾ãã°GHC/Haskellã¨ããè¨èªã®å ¨ä½åãå¤ããã«éããªããã¾ã æãã¬ç¸ã®ç®ç®ç¨ã§ãããªãããGHCã®ã¬ã³ã¼ãã®é²åã楽ãã¿ã«ãã¦ããã ãããã
2021/02/17 è¿½è¨ Adam Gundryæ°ãGHCã®ã¬ã³ã¼ãå¨ãã®å é¨ä»æ§ããã©ãã·ã¥ã¢ãããããããå®è£ ããªãã¼ã¹ãã¦åæ稿ãã¦ããã ãããããã¦ã¤ãã«16æ¥ãmasterã«ãã¼ã¸ãããã
å°æ¥
PolyKindsãStrictDataã¨åæ§ãNoFieldSelectorsã¯ã¢ã¸ã¥ã¼ã«åä½ã§ããæ¯ãèããå¶å¾¡ã§ããªããå°æ¥çã«ã¯ããã¼ã¿ååä½ã§æåãã³ã³ããã¼ã«ãããããã®æ¨ãããã¥ã¡ã³ãã«ãåæ ãããããã®ä»çµã¿ãå¿ è¦ã§ããã¨èãã¦ããã