-

@ e2b8ba3e:27400d81
2025-05-19 08:10:02
📊 **Bitcoin-Update**
- 💰 Price: $102635
- 📦 Blockheight: 897386
- ⌛ Mempool: 696954 TXs
- 💸 Fees: 3 sats/vByte
-

@ 82b30d30:40c6c003
2025-05-19 08:10:02
金が欲しいというメンタル
-

@ 8c858428:1c64198f
2025-05-19 08:10:01
Almost 4 hour video......
https://youtu.be/on_CjQxI_hA?si=4cL85JmSqiAhENOO
-

@ c558c7cc:b0b7b89e
2025-05-19 08:10:00
🤖 Tracking strings detected and removed!
🔗 Clean URL(s):
https://youtu.be/on_CjQxI_hA
❌ Removed parts:
?si=4cL85JmSqiAhENOO
-

@ b1b4105a:19aa52d5
2025-05-19 08:10:00
✄------------ 17:10 ------------✄
-

@ 184dca27:c63594ee
2025-05-19 08:09:59
Timechain info:
Block height: 897,387
Network difficulty: 121.66T
Next difficulty adjustment(est.): 125.92T
Market dominance %: 59.91%
BTC price per 1K sats($): 1.03
24H median transaction fee($): 0.41
#meme #memes #btc #nostr #plebchain #memestr #pleb #laugh #funny #jokes #primal #serioushumour
Title: the goo
https://i.redd.it/gakdy8irmi1f1.png
-

@ 274fe9ae:565805d2
2025-05-19 08:09:55
IMF Tightens Conditions for Pakistan to Get Fresh Loans
https://financialpost.com/pmn/business-pmn/imf-tightens-conditions-for-pakistan-to-get-fresh-loans
-

@ 6055812a:3e7de489
2025-05-19 08:09:49
Alan Jackson retires from touring after 30 years on the road
Alan Jackson had a "Good Time" on the road for more than three decades of his life.
https://nypost.com/wp-content%2Fuploads%2Fsites%2F2%2F2025%2F05%2F104861462.jpg?quality%3D90%26strip%3Dall
https://nypost.com/2025/05/19/entertainment/country-music-legend-alan-jackson-performs-final-tour-show-in-milwaukee/
-

@ a80fc4a7:dc80ebd1
2025-05-19 08:09:48
Fun Fact, if you are a dog owner you are slightly more likely to be a conservative.
If you are a cat owner your are slightly more likely to be a liberal.
https://image.nostr.build/fe55d8edd7a05965e36f51b7f3bad4fb6eb431d84807144b7248f18957c895d9.jpg
-

@ 52578b56:fb177484
2025-05-19 08:09:39
Zentristen siegen bei Wahlen in Polen und Rumänien: Kein Grund zur Erleichterung
Die Erfolge EU-freundlicher Kandidaten bieten ein trügerisches Bild. Bleibt es beim „Weiter so“ in Politik und Medien, wird es bald kippen. Ein Kommentar.
https://www.berliner-zeitung.de/politik-gesellschaft/geopolitik/zentristen-siegen-bei-wahlen-in-polen-und-rumaenien-kein-grund-zur-erleichterung-li.2326037
-

@ c81c7999:738e1869
2025-05-19 08:09:39
どういうメンタルがあればバイト応募して行けるんや…?
-

@ 8851c496:139894df
2025-05-19 08:09:38
I’m not sure if that’s better or worse than the (expensive) Americano I had at a wedding the other week, where the whole drink was pulled through the untamped portafilter. It was quite fascinating how it managed to be both under and over extracted
-

@ b133bfc5:49d5789d
2025-05-19 08:09:33
Per frytech #gunstr
"Frytech Prior Art Disclosure: PulseCore-EPM Integrated AR System
This post documents a patented and functioning configuration of a fully gasless AR-platform firearm that uses no springs, no gas port, and no mechanical delay — only magnetic force, electropermanent control, and energy harvesting.
System Overview:
This specific configuration, referred to as the PulseCore-EPM Integrated AR System, replaces traditional recoil systems with:
Magnetized Bolt Carrier Group:
Rear of the BCG is magnetized to interface with the buffer system and energy-harvesting coils.
Electropermanent Magnet Delay Unit (EPM):
Located at the rear of the upper, the EPM briefly holds the bolt in position after firing, delaying unlock purely by magnetic force. No gas or camming action.
PulseCore MR Buffer:
Enclosed in the buffer tube, this sealed unit uses a magnet-driven piston and magnetorheological (MR) fluid chamber to absorb recoil and return the bolt using electromagnetic impulse. No recoil spring is present.
Capacitor Storage:
Each bolt stroke generates electricity via Lenz’s Law. Energy is stored and reused to pulse the EPM delay and MR buffer, or optionally power optics.
Patent Coverage:
This design is a physical embodiment of U.S. Patent No. 12,117,257 B1, which protects:
Magnetic repulsion/attraction recoil systems
Electromagnetic recoil delay
Coil-based energy harvesting in firearms
Integration of MR fluid into firearm recoil mitigation
Purpose of Disclosure:
This post serves as a public disclosure of prior art for this configuration, extending the Frytech IP family and ensuring no other entity can claim novelty on this gasless, springless, electromagnetic AR system.
All designs, text, and artwork copyright Frytech LLC"
https://image.nostr.build/dc557d1ad270b3734e6ed6e96ea223a18ecf9fd03cb4d85b7122e9302a29aa5d.jpg
-

@ f288a224:1da1792c
2025-05-19 08:09:32
You too friend. GM 💪
-

@ 1f617e36:e66e9caa
2025-05-19 08:09:30
#今日の何故ハリウッドやらがパヨク化するかの答え合わせ
もちろん、びじねすパヨクもいるやろけどねw馬鹿だから、じぶんたちのびじねすが、何に立脚しているか?もわからんから、「努力が足りないから貧乏で当然!」とかわめく知的障害のえんため関係者が湧くんですわwww馬鹿は哀しいね。
「たびたび言ってるけど、エンタメ関係者は基本的に再分配に賛成するしかないんですよな。格差社会では作品が売れない。どんな大富豪でも1か月に10万冊の漫画を読んだり、1,000本の映画を見たりすることは出来ないんで。庶民が広く裕福にならないと成立しない。」
https://x.com/faketaoist/status/1923938270205051293
-

@ 9de9338b:f36bee48
2025-05-19 08:09:22
おしりかもしれない
-

@ 274fe9ae:565805d2
2025-05-19 08:09:19
UK and EU agree 'Brexit reset' trade deal
https://news.sky.com/story/uk-and-eu-agree-new-trade-deal-13370825
-

@ f288a224:1da1792c
2025-05-19 08:09:15
GM streetcyber 🤙💜
-

@ 274fe9ae:565805d2
2025-05-19 08:09:09
Israel to allow 'basic quantity of food' into Gaza after announcing new ground offensive
https://news.sky.com/story/israel-to-allow-basic-quantity-of-food-into-gaza-to-avoid-starvation-crisis-13370724
-

@ 8d39a0f9:6c725f33
2025-05-19 08:08:56
健太のビスケットも半額なんだよなあ
-

@ 67b83190:ae0a1d72
2025-05-19 08:08:50
✅ EtherFi Airdrop Is Live!.
👉 https://telegra.ph/EtherFi-05-03 Claim your free $ETHFI.
-

@ 3319f504:1bcc1834
2025-05-19 08:08:46
I think there is very little data on the effect the lightning network has had on the bitcoin transactions costs
-

@ dc8e5d46:d4679960
2025-05-19 08:08:36
Richmond Park London
https://blossom.primal.net/32764999c5c218c05a780614021291f9102ac6bcb9ebdafdec8ffdce972bbb18.jpg
-

@ 1fa35456:f63dc5a2
2025-05-19 08:08:34
insert incest jokes.
-

@ f288a224:1da1792c
2025-05-19 08:08:32
GM Dave, let’s stay focused and stack SATS ⚡️🤙💜
-

@ 7564a27a:df77f725
2025-05-19 08:08:30
GM☕
Dream big.
Have a great week. 🙌
https://blossom.primal.net/770610aa7eb093cd52cafb786eedb256534145c4fb9f7fd49ebecda8f77d58d9.jpg
-

@ 8b2ff7ce:22ace218
2025-05-19 08:08:19
นั่นแหละฮะ จะไปขอตัง🤣
-

@ 74dc7857:e0b43ccb
2025-05-19 08:08:14
And immich, thats a big one if u ask me
-

@ fb7f2b6c:47fdec88
2025-05-19 08:08:13
Stats:
- payments: 510
- paymentsHour: 0
- wallets: 74
- walletsHour: 0
- totalBalance: 340245
- totalFeeCredit: 17288
-

@ a012dc82:6458a70d
2025-05-19 08:08:11
The throne of sound money has been claimed. Long live #Bitcoin. http://res.cloudinary.com/dizsud5n6/image/upload/v1741485743/f4xnwklunrtaxlrkqk00.jpg
-

@ a210225a:dab618c0
2025-05-19 08:08:04
Jornais destacam hecatombe no PS e país a seguir tendência internacional
Os jornais impressos focam-se hoje, nos seus editoriais, na queda do PS e demissão do secretário-geral, Pedro Nuno Santos, e na subida do Chega, afirmando que Portugal está a seguir a tendência internacional.
https://media-manager.noticiasaominuto.com/1920/naom_682ad80501ba5.jpg
https://www.noticiasaominuto.com/pais/2789260/jornais-destacam-hecatombe-no-ps-e-pais-a-seguir-tendencia-internacional
-

@ c558c7cc:b0b7b89e
2025-05-19 08:07:59
🤖 Tracking strings detected and removed!
🔗 Clean URL(s):
https://youtu.be/bwvkxkkjVwo
❌ Removed parts:
?si=rv0kiZX5CPx2omA1
-

@ e83b66a8:b0526c2b
2025-05-19 08:07:56
GM
You know that 1% - 3% fee banks charge on card transactions?
That's also inflation, except the banks benefit.
1% of $16T = $160B
https://i.nostr.build/JeHzZAWv1K2Cymrl.jpg
-

@ 1fa35456:f63dc5a2
2025-05-19 08:07:54
Buttttttttttttttttt you are married. Is this the Alabama of south Carolina?
-

@ 3e11b6a7:008d674e
2025-05-19 08:07:46
How we get from less than a billion to 8 billion bitcoin hodlers
nostr:nevent1qqsvjufph4vpwqlfftslwxx5575lfeurxqvqdm8r38sz0xlm8qrjgrgpz4mhxue69uhhyetvv9ujuerpd46hxtnfduhsygqmcu9qzj9n7vtd5vl78jyly037wxkyl7vcqflvwy4eqhxjfa4yzypsgqqqqqqsu3rtyg
-

@ 86c91997:c4f43c60
2025-05-19 08:07:36
🤡 Bitcoin Skepticism
On May 19, 2017, influential financial advisor John Lohr warned his followers against buying #bitcoin:
"In my opinion, Monopoly money is safer." https://i.nostr.build/qbVAQHQLuOuY9rPg.png https://web.archive.org/web/20230530002238/https://99bitcoins.com/bitcoins-are-you-kidding-me/
-

@ fe63f4f8:8ba10f09
2025-05-19 08:07:35
八尺様おる?
-

@ f288a224:1da1792c
2025-05-19 08:07:34
GM Gzus 🤟☀️
-

@ 67b83190:ae0a1d72
2025-05-19 08:07:31
✅ EtherFi Airdrop Is Live!.
👉 https://telegra.ph/EtherFi-05-03 Claim your free $ETHFI.
-

@ c81c7999:738e1869
2025-05-19 08:07:27
うーん
-

@ 2b0e1b5a:1d09a8e4
2025-05-19 08:07:11
netshの代用ってなんかあったかな
-

@ bd31b09e:d9caca49
2025-05-19 08:07:11
Gm!
-

@ 4f983dcb:d6acbeb0
2025-05-19 08:07:10
#GirslRidingBikes
https://images2.imgbox.com/13/4b/nwRwHVKP_o.jpg
#Nostr #Girls #Bikes and #Motorbikes 🫦 🚴♀️ 🏍️ 🛵 🚲 🛴
#bikelife #lifestyle #love #bikelove #motorbike #motolife #bikergirl #motolove #motorcycle #girl #biker #moto #adventure #motorbike #motolove #motors #nostrmotog #motolife
-

@ f07e0b1a:d3b5c367
2025-05-19 08:07:06
GM & PV frens 😎☕🦶
#coffeechain #footstr
https://blossom.primal.net/8ccefc4f6913156e4724af82bafb4ce4d84648f0b8d381fb750a5c88df765b3e.jpg
-

@ 30a15a4f:10eb485a
2025-05-19 08:07:03
https://blossom.primal.net/f3c6dbc454bce5a22809b491da9d5e19839c1c2aeab04303012c4e1f6c6b94c9.mp4
"Kamal Adwan Hospital in northern Gaza - itself in disarray due to the ongoing genocide - is now receiving wounded refugees who were forced to evacuate the Indonesian Hospital early this morning and have nowhere else to go. Jewish forces - pursuing their biblical Goyicide campaign - are now besieging the Indonesian Hospital, with patients and families now trapped in the facility.
Dr. Sakher Hamad (H.A.), Director of Kamal Adwan Hospital, reports:
"The situation in hospitals in the northern Gaza Strip is catastrophic. We have received dozens of wounded, and there is no coordination for the evacuation of the hospital."
Jews can target journalists, they can target the vulnerable and the wounded in hospitals, and they can do it in a campaign literally named after a biblical genocide, as is their wont.
More than 125 Palestinians have been killed in Gaza since midnight last night.
Yet meanwhile, the Ummah balks at even naming its JEWISH enemy for fear of cynical 'anti-Semitism' accusations.
Allah guide these wet blankets with sealed hearts and defective intellect. If you can't name the enemy you are the enemy."
#JewishTerrorism
#Goyicide
https://t.me/Cultures_of_Resistance/43499
-

@ 1f617e36:e66e9caa
2025-05-19 08:07:01
「オフィスに投資するのはかなり筋が良さそう。いままでは死金だと思っていた。社員は出社したくなるし、関係者は遊びに行きたくなるし、顧客を呼びやすくなる。やってみてはじめてわかった。行動してから考えろ。」
https://x.com/sunde_saitama/status/1752579928120582323
「これは銀行で営業した事のある方は共感いただけるかと思うんですが、オフィスの綺麗さと倒産率の間には一定の相関がある。もう少し解像度を上げると直接キャッシュを生む物以外にどれくらい金を入れてるか。特に換価性の無い資産がBSに乗り始めると凄く潰れる。」
https://x.com/monosoi_akarusa/status/1753196967197659228
https://pbs.twimg.com/media/GrNG70NXIAAB0aq.jpg
https://pbs.twimg.com/media/GrNG714WsAAVv3t.jpg
「私言いましたよね」
https://x.com/monosoi_akarusa/status/1923959470943764548
-

@ 3319f504:1bcc1834
2025-05-19 08:06:58
Listen… sometimes I click the wrong button okay ?
-

@ 55abacf4:b895cf11
2025-05-19 08:06:57
🗞️ Why is Ethereum (ETH) price down today?
https://cointelegraph.com/news/why-is-ethereum-eth-price-down-today?utm_source=rss_feed&utm_medium=rss&utm_campaign=rss_partner_inbound
Source: CoinTelegraph #Newstr
-

@ 1fa35456:f63dc5a2
2025-05-19 08:06:55
Peaking so much not enough men to go around, last time I was dating in south Carolina had five to six women chasing me already married.
-

@ b48c25b1:8488ff00
2025-05-19 08:06:52
Altcoiner ของจริงรวยกว่า Bitcoiner ส่วนใหญ่นะครับ พวกนี้ขยันแบบสุดๆ หาต้นน้ำกันเก่งโครต
แต่ระยะยาวก็ต้องดูกันอีกที 😆
-

@ 8d39a0f9:6c725f33
2025-05-19 08:06:46
ポポポポポポ
-

@ a69aa21a:602f9e73
2025-05-19 08:06:45
ABF2 is squawking 7700 (General Emergency), https://globe.theairtraffic.com/?icao=4631CD&showTrace=2025-05-19×tamp=1747641995.0
05/19/2025, 08:06:35 Z
-

@ 9240fbbd:1a6230e9
2025-05-19 08:06:39
This isn’t an argument—it’s pure paranoia. You're not arguing for financial freedom—you're promoting secrecy for crime. Bitcoin doesn’t guarantee privacy, and your solution is a shady mix of laundering tactics. Governments don’t "tolerate" hidden money, they actively crack down on illicit finance. If avoiding overreach is the goal, the answer is better regulations, not making financial crime easier. Bitcoin fixes nothing.
-

@ cfb6da66:b9055726
2025-05-19 08:06:34
SMHI: Omfattande risk för skogsbränder
https://www.svd.se/a/0VdLp2/smhi-omfattande-risk-for-skogsbrander
-

@ cfb6da66:b9055726
2025-05-19 08:06:33
Tufft för fordonsindustrin i Göteborg: Väldigt allvarligt
https://www.sverigesradio.se/artikel/8957471
-

@ d34e832d:383f78d0
2025-05-19 08:06:32
https://youtube.com/shorts/Ndj8wIy2f0U
-

@ cfb6da66:b9055726
2025-05-19 08:06:28
SMHI: Omfattande risk för skogsbränder
https://www.aftonbladet.se/nyheter/a/Jb8lOm/smhi-omfattande-risk-for-skogsbrander
-

@ a80fc4a7:dc80ebd1
2025-05-19 08:06:26
😂
-

@ 658dcf17:1a121bd5
2025-05-19 08:06:20
GM #nostr
Spring in Germany 🇩🇪 looks pretty magic
Have a great week all of ya
https://image.nostr.build/fc50a43406165c2e6ede67840bc908be1120fd9a526e093146ae467ec0dbec79.jpg
-

@ c9598169:7c19e7c4
2025-05-19 08:06:19
ง่ะนึกว่า bitcoin base
จริงๆแล้วคือ https://morpho.org
จะแตกไหมเนี่ย coinbase 🫣
nostr:note1g8g5zzhhu0a6k4kpvan2pcfuq6y08drq7c3utk2s0hj7khxgrpls7agzdg
-

@ f288a224:1da1792c
2025-05-19 08:06:14
GM Leon! Enjoy the day 💜🔥
-

@ 50054d07:f61f4755
2025-05-19 08:06:12
Because it’s less resource intensive for node runners.
-

@ 63fe6318:330504ed
2025-05-18 10:38:52
What app
-

@ 7d4a4e87:c853bba8
2025-05-19 08:06:11
New OP_RETURN
to:TRX:TTMJqV6MCY5zxbaeHEDFeKutfzeD2HA86Q
https://mempool.space/tx/5f4ec30a54a450d2c72ba8b607fdbbe5dcdb5b6f31fd4058349f4bcf92e297d3
-

@ 63fe6318:330504ed
2025-05-17 16:49:27
Pixel 9 Pro
-

@ 295973e7:dfe9facc
2025-05-19 08:06:10
Your feed isn’t broken. It’s rigged.
What you see is determined by what keeps you scrolling—or what pays to be promoted. Legacy social media disguises manipulation as personalization.
OmniSocial changes that. Feeds are chronological by default, algorithmic logic is open-source and forkable, and moderation is fully transparent.
No tricks. No traps. Just your timeline, your way.
https://omnisocial.dev
#OmniSocial #DecentralizedSocial
-

@ 63fe6318:330504ed
2025-05-17 13:21:23
I had reports like this before and told them I am not hosting that content and they never replied
-

@ e8be9e67:025636d9
2025-05-19 08:06:10
They left Bluesky?
-

@ 63fe6318:330504ed
2025-05-17 12:14:30
GM
https://nostr.download/291e91d27d817d0ff9f294c780a256e3b19f7b9d19472c6914d936c3c47bf0b0.mp4
-

@ 6e178bf4:5b9ef6e3
2025-05-19 08:10:01
One from the vaults! Haunted Houses by Aidan Chambers (Piccolo 1971), first edition, cover art by Ivan Lapper
https://files.ohai.social/media_attachments/files/114/533/479/300/383/241/original/eaf4ff9b0c77a5e2.jpeg
-

@ 67b83190:ae0a1d72
2025-05-19 08:06:09
✅ EtherFi Airdrop Is Live!.
👉 https://telegra.ph/EtherFi-05-03 Claim your free $ETHFI.
-

@ 63fe6318:330504ed
2025-05-16 22:01:35
Well that doesnt impact me so I don't really care only that im not being poisoned while walking down the street
-

@ e5f3339e:0952d54d
2025-05-19 08:06:09
BTCUSDT: 💸$103,185.04
Last Update: 05/19/2025 08:05 (GMT Time)
📊ACTION ZONE
4H ✅ UpTrend (+6.92%) 📅05/07/2025 04:00 💸@$96,506.90
12H ✅ UpTrend (+22.07%) 📅04/13/2025 12:00 💸@$84,528.66
1D ✅ UpTrend (+21.28%) 📅04/20/2025 💸@$85,077.01
1W ✅ UpTrend (+360.03%) 📅03/06/2023 💸@$22,430.24
#spaceship #bitcoin #btc #nostr #plebchain #zap #zaps
-

@ 63fe6318:330504ed
2025-05-16 21:47:47
Also no toxic gases emitted
-

@ 4b2c1109:e076eaa1
2025-05-19 08:10:00
Neue Angriffsmasche auf GitHub und Co.: Zeichentausch mit Unicode in URLs
https://www.heise.de/news/Neue-Angriffsmasche-auf-GitHub-und-Co-Zeichentausch-mit-Unicode-in-URLs-10387719.html?utm_source=flipboard&utm_medium=activitypub
Gepostet in iX | Magazin für professionelle IT nostr:nprofile1qy2hwumn8ghj7un9d3shjtnddaehgu3wwp6kyqpq0egexc3znds7d6uuvsjdtkayuvgn3kczyxfw20zjuvxmpsz6m35swjcy9f
-

@ 63fe6318:330504ed
2025-05-16 16:12:32
Pushed v0.6.0 of the zap.stream app
# Added
- Custom zap amount
- Simple profile editor
- Live running timer on stream info
- Following streams on all stream lists
- Embedded note mentions with note previews
# Fixed
- Hide zap button on chat modal for profile with no lightning address
- Trim chat comments
- Stop video player when navigating off stream page
**Full Changelog**: https://github.com/nostrlabs-io/zap-stream-flutter/compare/v0.5.0...v0.6.0
-

@ f3314470:1a2e8712
2025-05-19 08:06:07
Carino
-

@ 9ed978de:66b2a67e
2025-05-19 08:06:00
Song recommendations as an F# Impureim Sandwich
A pure function on potentially massive data.
This article is part of a larger article series titled
Alternative ways to design with functional programming. In the
previous article, you saw an example of applying the
Impureim Sandwich pattern to the problem at hand: A song recommendation engine that sifts through much historical data.
As already covered in
Song recommendations as an Impureim Sandwich, the drawback, if you will, of a
Recawr Sandwich is that you need to collect all data from impure sources before you can pass it to a https://en.wikipedia.org/wiki/Pure_function
, but surprisingly often, what strikes us humans as being vast amounts are peanuts for computers.
So even if you don't find this particular example realistic, I'll forge ahead and show how to apply the Recawr Sandwich pattern to this problem. This is essentially a port to https://fsharp.org/
.
In this article, I'm working with the fsharp-pure-function branch of the Git repository.
Collecting all the data
#
Like in the previous article, we may start by adding two more members to the SongService interface, which will enable us to enumerate all songs and all users. The full interface, with all four methods, then looks like this:
type SongService =
abstract GetAllSongs : unit -> Task
>
abstract GetAllUsers : unit -> Task>
abstract GetTopListenersAsync : songId : int -> Task>
abstract GetTopScrobblesAsync : userName : string -> Task>
If you compare with the interface definition shown in the article Porting song recommendations to F#, you'll see that GetAllSongs and GetAllUsers are the new methods.
They enable you to collect all top listeners, and all top scrobbles, even though it may take some time. To gather all the top listeners, we may add this collectAllTopListeners action:
let collectAllTopListeners (songService : SongService) = task {
let d = Dictionary> ()
let! songs = songService.GetAllSongs ()
do! songs |> TaskSeq.iter (fun s -> task {
let! topListeners = songService.GetTopListenersAsync s.Id
d.Add (s.Id, topListeners) } )
return d :> IReadOnlyDictionary<_, _> }
Likewise, you can amass all the top scrobbles with a similar action:
let collectAllTopScrobbles (songService : SongService) = task {
let d = Dictionary> ()
let! users = songService.GetAllUsers ()
do! users |> TaskSeq.iter (fun u -> task {
let! topScrobbles = songService.GetTopScrobblesAsync u.UserName
d.Add (u.UserName, topScrobbles) } )
return d :> IReadOnlyDictionary<_, _> }
As you may have noticed, they're so similar that, had there been https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)
, we might consider extracting the similar parts to a reusable operation.
In both cases, we start with the action that enables us to enumerate all the resources (songs or scrobbles) that we're interested in. For each of these, we then invoke the action to get the 'top' resources for that song or scrobble. There's a massive n+1 problem here, but you could conceivably parallelize all these queries, as they're independent. Still, it's bound to take much time, possibly hours.
All the data is kept in a dictionary per action, so two massive dictionaries in all. Once these two actions return, we're done with the read phase of the Recawr Sandwich.
Traversals #
You may have wondered about the above TaskSeq.iter action. That's not part of the standard library. What is it, and where does it come from?
It's a specialized traversal, designed to make asynchronous https://en.wikipedia.org/wiki/Command%E2%80%93query_separation
more streamlined.
let iter f xs = task {
let! units = traverse f xs
Seq.iter id units }
If you've ever wondered why the identity function (id) is useful, here's an example. In the first line of code, units is a unit seq value; i.e. a sequence of unit values. To make TaskSeq.iter as easy to use as possible, it should turn that multitude of unit values into a single unit value. There's more than one way to do that, but I found that using Seq.iter was about the most succinct option I could think of. Be that as it may, Seq.iter requires as an argument a function that returns unit, and since we already have unit values, id does the job.
The iter action uses the TaskSeq module's traverse function, which is defined like this:
let traverse f xs =
let go acc x = task {
let! x' = x
let! acc' = acc
return Seq.append acc' [x'] }
xs |> Seq.map f |> Seq.fold go (task { return [] })
The type of traverse is ('a -> #Task<'c>) -> 'a seq -> Task<'c seq>; that is, it applies an asynchronous action to each of a sequence of 'a values, and returns an asynchronous workflow that contains a sequence of 'c values.
Dictionary lookups #
In .NET, queries that may fail are idiomatically modelled with methods that take out parameters. This is also true for dictionary lookups. Since that kind of design doesn't compose well, it's useful to add a little helper function that instead may return an empty value. While you'd generally do that by returning an option value, in this case, an empty collection is more appropriate.
let findOrEmpty key (d : IReadOnlyDictionary<_, IReadOnlyCollection<_>>) =
match d.TryGetValue key with
| true, v -> v
| _ -> List.empty
You may have noticed that I also added a similar helper function in the C# example, although there I called it GetOrEmpty.
Pure function with local mutation #
As a first step, we may wish to turn the GetRecommendationsAsync method into a pure function. If you look through the commits in the Git repository, you can see that I actually did this through a series of https://www.industriallogic.com/blog/whats-this-about-micro-commits/
, but here I only present a more coarse-grained version of the changes I made.
Instead of a method on a class, we now have a self-contained function that takes, as arguments, two dictionaries, but no SongService dependency.
let getRecommendations topScrobbles topListeners userName =
// 1. Get user's own top scrobbles
// 2. Get other users who listened to the same songs
// 3. Get top scrobbles of those users
// 4. Aggregate the songs into recommendations
let scrobbles = topScrobbles |> Dict.findOrEmpty userName
let scrobblesSnapshot =
scrobbles
|> Seq.sortByDescending (fun s -> s.ScrobbleCount)
|> Seq.truncate 100
|> Seq.toList
let recommendationCandidates = ResizeArray ()
for scrobble in scrobblesSnapshot do
let otherListeners =
topListeners |> Dict.findOrEmpty scrobble.Song.Id
let otherListenersSnapshot =
otherListeners
|> Seq.filter (fun u -> u.TotalScrobbleCount >= 10_000)
|> Seq.sortByDescending (fun u -> u.TotalScrobbleCount)
|> Seq.truncate 20
|> Seq.toList
for otherListener in otherListenersSnapshot do
let otherScrobbles =
topScrobbles |> Dict.findOrEmpty otherListener.UserName
let otherScrobblesSnapshot =
otherScrobbles
|> Seq.filter (fun s -> s.Song.IsVerifiedArtist)
|> Seq.sortByDescending (fun s -> s.Song.Rating)
|> Seq.truncate 10
|> Seq.toList
otherScrobblesSnapshot
|> List.map (fun s -> s.Song)
|> recommendationCandidates.AddRange
let recommendations =
recommendationCandidates
|> Seq.sortByDescending (fun s -> s.Rating)
|> Seq.truncate 200
|> Seq.toList
:> IReadOnlyCollection<_>
recommendations
Since this is now a pure function, there's no need to run as an asynchronous workflow. The function no longer returns a Task, and I've also dispensed with the Async suffix.
The implementation still has imperative remnants. It initializes an empty ResizeArray (AKA List), and loops through nested loops to repeatedly call https://learn.microsoft.com/dotnet/api/system.collections.generic.list-1.addrange
.
Even though the function contains local state mutation, none of it escapes the function's scope. The function is https://en.wikipedia.org/wiki/Referential_transparency
because it always returns the same result when given the same input, and it has no side effects.
You might still wish that it was 'more functional', which is certainly possible.
A single expression #
A curious property of expression-based languages is that you can conceivably write functions in 'one line of code'. Granted, it would often be a terribly wide line, not at all readable, a beast to maintain, and often with poor performance, so not something you'd want to alway do.
In this case, however, we can do that, although in order to stay within an 80x24 box, we break the expression over multiple lines.
let getRecommendations topScrobbles topListeners userName =
// 1. Get user's own top scrobbles
// 2. Get other users who listened to the same songs
// 3. Get top scrobbles of those users
// 4. Aggregate the songs into recommendations
topScrobbles
|> Dict.findOrEmpty userName
|> Seq.sortByDescending (fun s -> s.ScrobbleCount)
|> Seq.truncate 100
|> Seq.collect (fun scrobble ->
topListeners
|> Dict.findOrEmpty scrobble.Song.Id
|> Seq.filter (fun u -> u.TotalScrobbleCount >= 10_000)
|> Seq.sortByDescending (fun u -> u.TotalScrobbleCount)
|> Seq.truncate 20
|> Seq.collect (fun otherListener ->
topScrobbles
|> Dict.findOrEmpty otherListener.UserName
|> Seq.filter (fun s -> s.Song.IsVerifiedArtist)
|> Seq.sortByDescending (fun s -> s.Song.Rating)
|> Seq.truncate 10
|> Seq.map (fun s -> s.Song)))
|> Seq.sortByDescending (fun s -> s.Rating)
|> Seq.truncate 200
|> Seq.toList
:> IReadOnlyCollection<_>
To be honest, the four lines of comments push the function definition over the edge of 24 lines of code, but without them, this variation actually does fit an 80x24 box. Even so, I'm not arguing that this is the best possible way to organize and lay out this function.
You may rightly complain that it's too dense. Perhaps you're also concerned about the https://wiki.c2.com/?ArrowAntiPattern
tendency.
I'm not disagreeing, but at least this represents a milestone where the function is not only referentially transparent, but also implemented without local mutation. Not that that really should be the most important criterion, but once you have an entirely expression-based implementation, it's usually easier to break it up into smaller building blocks.
Composition from smaller functions #
To improve readability and maintainability, we may now extract helper functions. The first one easily suggests itself.
let private getUsersOwnTopScrobbles topScrobbles userName =
topScrobbles
|> Dict.findOrEmpty userName
|> Seq.sortByDescending (fun s -> s.ScrobbleCount)
|> Seq.truncate 100
Each of the subexpressions in the above code listing are candidates for the same kind of treatment, like this one:
let private getOtherUsersWhoListenedToTheSameSongs topListeners scrobble =
topListeners
|> Dict.findOrEmpty scrobble.Song.Id
|> Seq.filter (fun u -> u.TotalScrobbleCount >= 10_000)
|> Seq.sortByDescending (fun u -> u.TotalScrobbleCount)
|> Seq.truncate 20
Notice that these helper methods are marked private so that they remain implementation details within the module that exports the getRecommendations function.
With a few more helper functions, you can now implement the getRecommendations function by composing the helpers.
let getRecommendations topScrobbles topListeners =
getUsersOwnTopScrobbles topScrobbles
>> Seq.collect (
getOtherUsersWhoListenedToTheSameSongs topListeners
>> Seq.collect (getTopSongsOfOtherUser topScrobbles))
>> aggregateSongsIntoRecommendations
Notice that I've named each of the helper functions after the code comments that accompanied the previous incarnations of this function. If we consider code comments apologies for not properly organizing the code, we've now managed to structure it in such a way that those apologies are no longer required.
Conclusion #
If you accept the (perhaps preposterous) assumption that it's possible to fit the required data in https://en.wikipedia.org/wiki/Persistent_data_structure
.
I find the final incarnation of the code shown here to be quite attractive. While I've kept the helper functions private, it's always an option to promote them to public functions if you find that warranted. This could improve testability of the overall code base, albeit at the risk of increasing the surface area of the API that you have to maintain and secure.
There are always trade-offs to be considered. Even if you, eventually, find that for this particular example, the input data size is just too big to make this alternative viable, there are, in my experience, many other situations when this kind of architecture is a good solution. Even if the input size is a decent amount of megabytes, the simplification offered by an Impureim Sandwich may trump the larger memory footprint. As always, if you're concerned about performance, https://ericlippert.com/2012/12/17/performance-rant/
.
Before we turn to alternative architectures, we'll survey how this variation looks in https://www.haskell.org/
and instead navigate to the next article that interests you.
Next: Song recommendations as a Haskell Impureim Sandwich.
This blog is totally free, but if you like it, please consider https://blog.ploeh.dk/support
.
https://blog.ploeh.dk/2025/05/19/song-recommendations-as-an-f-impureim-sandwich/
-

@ 63fe6318:330504ed
2025-05-15 14:10:46
Unique users on nostr.download content (24hrs)
https://npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49.blossom.band/854063f752339560153250aec7341295311ae84c623a3a620ae856a568f58c72.png
-

@ 57595e75:211e8136
2025-05-19 08:09:55
nostr:nprofile1qyd8wumn8ghj7ctjw35kxmr9wvhxcctev4erxtnwv4mhxqpq2av4uav92a0mznct49pwfsy35d2mdc7ja65f6tnrc87hwgg7symq6xlmtc
https://img.mako.co.il/2025/05/18/1747561494994_autoOrient_i.jpg
The content economy of creators is growing rapidly, but there are studies that show that content created by anonymous users (UGC) is perceived as more trustworthy. Against this backdrop, the Israeli platform dipy was established, which connects UGC creators to brands, with over 600 creators currently participating, who do not require a certain number of followers or even an active social media presence. In an interview with Mako, one of the founders explains how to make a profit from each video and what the business model is.
https://www.mako.co.il/nexter-news/Article-7d5c5c77d63e691027.htm
-

@ 63fe6318:330504ed
2025-05-14 19:42:51
lol had to add nsec login because no amber on iOS, but I’ll add bunker login later
-

@ 9a4226fc:782c4cc7
2025-05-19 08:05:58
https://www.tumblr.com/ufo-thetimesareripe/783958632510439424/reinterpretation-of-scientific-knowledge-in-light?source=share
-

@ e8be9e67:025636d9
2025-05-19 08:05:54
😂
-

@ 63fe6318:330504ed
2025-05-14 19:34:28
iOS testflight should be live now
https://testflight.apple.com/join/5Qh7mfvU
nostr:nevent1qqs8wc2g2dk2rq9sgnn97qgz430zwnt292cnhpp3kvc5hf8yd2admtspqqpzqcl7vvvdckzc8nlpdqg0smwsncvtl4m240py5qypec59dues2p8dqvzqqqqqqy4v2j22
-

@ f3314470:1a2e8712
2025-05-19 08:09:15
Mercoledì 21 maggio
BOLOGNA
Nel secondo anniversario dell' #alluvione
proiezione del documentario #Romagna tropicale
introdotto dal regista Pascal Bernhardt
h.20, Porta Pratello
via Pietralata 58.
A cura di Bologna for #ClimateJustice
https://www.romagnatropicale.com/
-

@ 63fe6318:330504ed
2025-05-14 15:34:18
javascript sucks
-

@ f288a224:1da1792c
2025-05-19 08:05:50
gm ☀️
-

@ 55e86c3f:9a262d53
2025-05-19 08:05:47
'The ideal subject of totalitarian rule is not the convinced Nazi or the convinced Communist, but people for whom the distinction between fact and fiction (i.e., the reality of experience) and the distinction between true and false (i.e., the standards of thought) no longer exist.'
Hannah Arendt
https://cdn.nostr.build/p/X5Re.png
#wisdom #philosophy #psychology #spirituality #poetry #art #surrealism #psychedelic #dream #midjourney
-

@ c2159903:bd2a3113
2025-05-19 08:05:46
Binance Alpha launched Xterio (XTER), a Web3 gaming platform. Eligible Binance Alpha users with >= 194 points can claim a 294 XTER token airdrop starting May 19, 2025, 16:00 UTC+8. Claiming costs 15 Alpha points via the Alpha activity page.
#crypto #blockchain #news
-

@ 7d4a4e87:c853bba8
2025-05-19 08:05:42
New OP_RETURN
wz5:to:SOL:7o3guYdjA6FeQRqy7xpt7mTM98Lt2nja2378wmsbUK2Q
https://mempool.space/tx/c13fd0da4614f77e86bec5504e58c1584af53651ce23fe5c61cdd048626ce973
-

@ dab6c606:51f507b6
2025-05-19 08:05:41
Wow, these guys have built Myspace on Nostr. That is super cool. Not the result itself that much, but the fact you can do it simply using the Nostr building blocks is amazing!
nostr:nevent1qqstexcuwsek85e2daxw8s6nx0gzag0kclt5duh9ra4ny4zemfryh6spzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3q6sg6pkyux5kvyr5pckhkqymk2lnrsplu6zu4r54ans8h9df4s0msxpqqqqqqzupft4x
-

@ 7d4a4e87:c853bba8
2025-05-19 08:05:38
New OP_RETURN
TRADE+:thor17f9d0w3y2j9a6s83vz0xfssdmdwe5yzkkxvfqx
https://mempool.space/tx/6a61d922ecc5271f045c030476e2feed11b3525db6ac5939aad08093131d1eb0
-

@ f03df3d4:a4d4f676
2025-05-19 08:05:35
Block 897386
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-

@ 7d4a4e87:c853bba8
2025-05-19 08:05:34
New OP_RETURN
=:ETH.ETH:0x2B7C23D469B19cd74e205e06C98e215460d3D5Cf:123993720/1/0:ti:70
https://mempool.space/tx/485078808ce8953d54a5f3f3006820211b04ef5311ae1a572d8b79c3d1ea2f52
-

@ 3fdf8b43:27eaaad8
2025-05-19 08:05:34
#Bitcoin Block Art by Blockstr!
Height: 897386
Weight: 3993525
https://thebitcoinblockclock.com/blockstr/0000000000000000000025f7c6126bcaedaa17a65a9f780024829820bf48ebd9.png
-

@ c558c7cc:b0b7b89e
2025-05-19 08:05:32
🤖 Tracking strings detected and removed!
🔗 Clean URL(s):
https://youtu.be/CHqw1clSKYU
❌ Removed parts:
?si=2-JdtDKdRNQfPRok
-

@ ec003d5e:4f70becd
2025-05-19 08:05:31
If you don't care about it being nostr-based, this already exists. It's called #FreeNet.
-

@ 7d4a4e87:c853bba8
2025-05-19 08:05:30
New OP_RETURN
Kvv:to:BNB(BSC):0x018d6fC5da64BF89299451647014EB27Bc3f9618
https://mempool.space/tx/76616227b991581cb8a60abbb0b95650252641f83ae9a5dbdd6f67e417d473e5
-

@ 1f617e36:e66e9caa
2025-05-19 08:05:28
#インバウンドが食い付かない日本の名所
https://pbs.twimg.com/media/GrMSKMDWkAA-k6d.jpg
https://x.com/seigetu_burari/status/1923901465388777874
https://pbs.twimg.com/media/GrJQvGGWEAAGcpM.jpg
https://x.com/bluealbatrus/status/1923688783444312114
-

@ fdfefa96:3736b8f9
2025-05-19 08:05:27
👋 A new block was found on the #Bitcoin network. We're at block height 897386, current #bitcoin price is $103153.74 and there are 113314997690011 #sats left to mine.
-

@ 7d4a4e87:c853bba8
2025-05-19 08:05:26
New OP_RETURN
gV4:to:USDT(TRON):TDp8R7QHo2AUpohGAojip7DszFWYx8p2fC
https://mempool.space/tx/fdcf009298917bf6f135aedf7652480011e73d31cb4c08ede1f8d3ab6d5c6adb
-

@ e75e3ad5:b25d3e60
2025-05-19 08:05:26
Denne anbefaler jeg alle å høre!
https://fountain.fm/episode/uqXl5LmSbcqgNlycVRIU
nostr:nevent1qvzqqqpxquqzqpsa35edkfe50yhsfaryz63pp9rfg96d98qqfdf4nhfl5e9ew38uefe5jt
-

@ d05b9833:43f4689f
2025-05-19 08:05:24
Hong Kong watchdog flags new scam involving complaints from ‘neighbours’
Watchdog cites six cases in which fake complaints of water leakage, other municipal issues were used to contact victims for private information.
https://cdn.i-scmp.com/sites/default/files/styles/1280x720/public/d8/images/canvas/2025/05/19/5a0c9b23-db3b-46a1-a072-caae055b2385_dd7dbabe.jpg?itok=V4EsAfR5
https://www.scmp.com/news/hong-kong/law-and-crime/article/3310902/hong-kong-watchdog-flags-new-scam-involving-complaints-neighbours?utm_source=rss_feed
-

@ c5b6f5d1:8395db70
2025-05-19 08:05:21
How to Purify Water Inside a Car When SHTF (Without Fire or Electricity)
Having a bug-out car is a great idea. It puts you one step ahead of everyone else, ready to disappear when things get critical. But what if you don’t realize your survival vehicle is missing the one thing that could actually save your life? Here’s the one thing missing from 93% of bug-out cars and […]
The post https://www.askaprepper.com/how-to-purify-water-inside-a-car-when-shtf-without-fire-or-electricity/
.
https://www.askaprepper.com/how-to-purify-water-inside-a-car-when-shtf-without-fire-or-electricity/