bgl gwyng

@bgl@hackers.pub · 64 following · 64 followers

슈티를 함께 만들 팀을 만들고 있습니다. 관심 있으신 분, 또는 잘 모르겠지만 이야기를 나눠보고 싶은 분도 bgl@gwyng.com으로 편하게 연락주세요.

GitHub
@bglgwyng
shootee
www.shootee.io
0

높은 목표를 가진 개발자라도 결국엔 아주 사소한 동기로 움직이는거 같다.

나같은 경우엔, 완벽한 프로그래밍 언어를 만드는 것이 목표인데(가능한지는 차치하고), 완벽하다는건 나말고 다른 누군가가 같은 문제 의식을 가진다면 똑같이 그곳에 다다를 거란걸 의미한다. 그 프로그래밍 언어의 설계에서 내 마음대로 결정할수 있는 부분은 없을 것이다. 설계에서 최적의 선택지만을 택해야 완벽할테니까 말이다. 그때가선 그 선택들이 너무 자명해서, 내겐 처음부터 선택의 여지가 없었다고 느낄것이다.

그럼에도 내가 결정할 수 있을 부분이 있기는한데, 그 언어의 이름에 뜬금없이 우리집 강아지 이름을 붙인다던가 하는 것이다. 이게 그 사소한 동기다.

0

디지털 가드닝에 관심이 많은 개발자입니다.
- 특히 위키 형식의 문서 관리, knowledge graph 구조의 시각화에 관심이 있어요.
- kodingwarrior.github.io/wiki

Neovim 이라는 텍스트 에디터에 굉장히 꽂혀있습니다.
- 과몰입한 나머지 플러그인까지 개발해본 경험이 있어요.
- 한국어권 개발자를 위한 Vim 디스코드를 운영중입니다 (vim.kr)

프로그래밍을 하는 행위 자체를 좋아합니다.
- 프로그래밍으로 퍼즐을 푸는 행위를 좋아했고, 비슷한 흔적을 가진 사람들에게 친밀감을 느낍니다.

0
0
0
0
0

해커펍은 퍼머링크로 아카이빙 참조하기 최적이라 생각해서 앞으로 기술을 다루며 기록 및 참조하는 용도로 잘 사용하려고 합니다.

트위터는 나중에 다른 사람에게 보여줄 참조용으로 쓰기에는 너무 정보 대비 소음이 많은 특성 때문에 잘 맞지 않는다고 생각합니다.

0

그동안(10+년;;) git이 엄청 잘만든 물건 같지는 않다고 생각하며 대충 쓰고있었는데, 요즘 branch 개념 자체가 근본적인 실수란 생각이 들기 시작했다. branch 대신에 변경의 시작과 끝, 양 끝점을 가지는 interval을 쓰는게 맞는거 같다(카테고리 이론의 작은 교훈: primitive는 양 끝점을 가지는게 좋다).

git을 쓰면 히스토리 길어진다고 squash merge 등을 하는데, (나도 하지만) 사실 기껏 만들어놓은 히스토리를 뭉개버리는 말도 안되는 동작이다. 만약 interval을 쓴다면 히스토리는 그대로 남기고 UI 단에서 fold/unfold 등을 해줄수 있을 것이다.

Darcs 등이 interval에 기초하는데, 지금은 일이 너무 바빠서 시도할 여유가 없다. 한번 숨고를 시간이 주어지면 멀쩡한 VCS를 탐색하는 시간을 가질것이다.

0

📢 Hackers' Pub 초대 시스템 오픈!

Hackers' Pub에 초대 시스템이 적용되었습니다. 이제 설정초대 페이지에서 지인들을 초대할 수 있습니다.

주요 내용:

  • 초대장 3장 지급: 기존 회원분들께 3장의 초대장이 지급되었습니다.
  • 초대 방법: 설정 → 초대 페이지에서 초대 링크를 생성하여 공유하거나, 이메일 주소를 입력하여 초대할 수 있습니다.
  • 추가 초대: 초대장은 향후 비정기적으로 추가될 예정입니다.
  • 자동 팔로: 초대자와 피초대자는 자동으로 상호 팔로됩니다. (언팔로 가능.)

Hackers' Pub의 퀄리티를 유지하고, 더욱 풍성한 기술 논의를 위해 신중한 초대를 부탁드립니다.

궁금한 점이나 건의사항은 답글로 남겨주세요.

Hackers' Pub 커뮤니티 성장에 많은 참여 부탁드립니다!

Hackers' Pub 웹사이트의 설정 메뉴에서 “초대 (3)”이 선택된 화면입니다. 페이지 제목은 “Hackers' Pub에 친구를 초대하세요. 현재 3장의 초대장이 남아 있습니다”로 표시되어 있습니다. 아래에는 이메일 주소를 입력하는 필드와 “이메일 주소는 초대장을 받을 때 뿐만 아니라, 계정에 로그인 할 때도 사용됩니다”라는 안내 문구가 있습니다. 그 아래에는 “추가 메시지”라는 제목의 텍스트 영역과 “초대장을 받는 친구가 볼 수 있는 메시지입니다”라는 설명이 있습니다. 하단에는 “초대장 보내기” 버튼과 “초대한 사람” 목록이 표시되어 있으며, “洪 民意 (Hong Minhee) (@hongminhee@hackers.pub)”라는 이름과 아이디가 적혀 있습니다.
0
0
0
0
0
0

@tirr티르 저도 서브타이핑 기반인 TS에 상대적으로 쉽게 도입할 기능이 https://github.com/microsoft/TypeScript/issues/13219 이렇게 오랫동안 진행안되는게 불만입니다. 막상 TS 이펙트 라이브러리들은 |로 흉내내서 잘 쓰고 있더라고요. Haskell처럼 대수적 이펙트는 구현할수있지만 서브타이핑 기반은 아닌 언어에선, 서브타이핑 흉내낸다고 타입레벨 차력쇼하고 있는데 맞는 방향인지 모르겠습니다.

0
0

bgl gwyng shared the below article:

Revisiting Java's Checked Exceptions: An Underappreciated Type Safety Feature

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Despite their bad reputation in the Java community, checked exceptions provide superior type safety comparable to Rust's Result<T, E> or Haskell's Either a b—we've been dismissing one of Java's best features all along.

Introduction

Few features in Java have been as consistently criticized as checked exceptions. Modern Java libraries and frameworks often go to great lengths to avoid them. Newer JVM languages like Kotlin have abandoned them entirely. Many experienced Java developers consider them a design mistake.

But what if this conventional wisdom is wrong? What if checked exceptions represent one of Java's most forward-thinking features?

In this post, I'll argue that Java's checked exceptions were ahead of their time, offering many of the same type safety benefits that are now celebrated in languages like Rust and Haskell. Rather than abandoning this feature, we should consider how to improve it to work better with modern Java's features.

Understanding Java's Exception Handling Model

To set the stage, let's review how Java's exception system works:

  • Unchecked exceptions (subclasses of RuntimeException or Error): These don't need to be declared or caught. They typically represent programming errors (NullPointerException, IndexOutOfBoundsException) or unrecoverable conditions (OutOfMemoryError).

  • Checked exceptions (subclasses of Exception but not RuntimeException): These must either be caught with try/catch blocks or declared in the method signature with throws. They represent recoverable conditions that are outside the normal flow of execution (IOException, SQLException).

Here's how this works in practice:

// Checked exception - compiler forces you to handle or declare it
public void readFile(String path) throws IOException {
    Files.readAllLines(Path.of(path));
}

// Unchecked exception - no compiler enforcement
public void processArray(int[] array) {
    int value = array[array.length + 1]; // May throw ArrayIndexOutOfBoundsException
}

The Type Safety Argument for Checked Exceptions

At their core, checked exceptions are a way of encoding potential failure modes into the type system via method signatures. This makes certain failure cases part of the API contract, forcing client code to explicitly handle these cases.

Consider this method signature:

public byte[] readFileContents(String filePath) throws IOException

The throws IOException clause tells us something critical: this method might fail in ways related to IO operations. The compiler ensures you can't simply ignore this fact. You must either:

  1. Handle the exception with a try-catch block
  2. Propagate it by declaring it in your own method signature

This type-level representation of potential failures aligns perfectly with principles of modern type-safe programming.

Automatic Propagation: A Hidden Advantage

One often overlooked advantage of Java's checked exceptions is their automatic propagation. Once you declare a method as throws IOException, any exception that occurs is automatically propagated to the caller without additional syntax.

Compare this with Rust, where you must use the ? operator every time you call a function that returns a Result:

// Rust requires explicit propagation with ? for each call
fn read_and_process(path: &str) -> Result<(), std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    process_content(&content)?;
    Ok(())
}

// Java automatically propagates exceptions once declared
void readAndProcess(String path) throws IOException {
    String content = Files.readString(Path.of(path));
    processContent(content); // If this throws IOException, it's automatically propagated
}

In complex methods with many potential failure points, Java's approach leads to cleaner code by eliminating the need for repetitive error propagation markers.

Modern Parallels: Result Types in Rust and Haskell

The approach of encoding failure possibilities in the type system has been adopted by many modern languages, most notably Rust with its Result<T, E> type and Haskell with its Either a b type.

In Rust:

fn read_file_contents(file_path: &str) -> Result<Vec<u8>, std::io::Error> {
    std::fs::read(file_path)
}

When calling this function, you can't just ignore the potential for errors—you need to handle both the success case and the error case, often using the ? operator or pattern matching.

In Haskell:

readFileContents :: FilePath -> IO (Either IOException ByteString)
readFileContents path = try $ BS.readFile path

Again, the caller must explicitly deal with both possible outcomes.

This is fundamentally the same insight that motivated Java's checked exceptions: make failure handling explicit in the type system.

Valid Criticisms of Checked Exceptions

If checked exceptions are conceptually similar to these widely-praised error handling mechanisms, why have they fallen out of favor? There are several legitimate criticisms:

1. Excessive Boilerplate in the Call Chain

The most common complaint is the boilerplate required when propagating exceptions up the call stack:

void methodA() throws IOException {
    methodB();
}

void methodB() throws IOException {
    methodC();
}

void methodC() throws IOException {
    // Actual code that might throw IOException
}

Every method in the chain must declare the same exception, creating repetitive code. While automatic propagation works well within a method, the explicit declaration in method signatures creates overhead.

2. Poor Integration with Functional Programming

Java 8 introduced lambdas and streams, but checked exceptions don't play well with them:

// Won't compile because map doesn't expect functions that throw checked exceptions
List<String> fileContents = filePaths.stream()
    .map(path -> Files.readString(Path.of(path))) // Throws IOException
    .collect(Collectors.toList());

This forces developers to use awkward workarounds:

List<String> fileContents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new UncheckedIOException(e); // Wrap in an unchecked exception
        }
    })
    .collect(Collectors.toList());

3. Interface Evolution Problems

Adding a checked exception to an existing method breaks all implementing classes and calling code. This makes evolving interfaces over time difficult, especially for widely-used libraries and frameworks.

4. Catch-and-Ignore Anti-Pattern

The strictness of checked exceptions can lead to the worst possible outcome—developers simply catching and ignoring exceptions to make the compiler happy:

try {
    // Code that might throw
} catch (Exception e) {
    // Do nothing or just log
}

This is worse than having no exception checking at all because it provides a false sense of security.

Improving Checked Exceptions Without Abandoning Them

Rather than abandoning checked exceptions entirely, Java could enhance the existing system to address these legitimate concerns. Here are some potential improvements that preserve the type safety benefits while addressing the practical problems:

1. Allow lambdas to declare checked exceptions

One of the biggest pain points with checked exceptions today is their incompatibility with functional interfaces. Consider how much cleaner this would be:

// Current approach - forced to handle or wrap exceptions inline
List<String> contents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    })
    .collect(Collectors.toList());

// Potential future approach - lambdas can declare exceptions
List<String> contents = filePaths.stream()
    .map((String path) throws IOException -> Files.readString(Path.of(path)))
    .collect(Collectors.toList());

This would require updating functional interfaces to support exception declarations:

@FunctionalInterface
public interface Function<T, R, E extends Exception> {
    R apply(T t) throws E;
}

2. Generic exception types in throws clauses

Another powerful enhancement would be allowing generic type parameters in throws clauses:

public <E extends Exception> void processWithException(Supplier<Void, E> supplier) throws E {
    supplier.get();
}

This would enable much more flexible composition of methods that work with different exception types, bringing some of the flexibility of Rust's Result<T, E> to Java's existing exception system.

3. Better support for exception handling in functional contexts

Unlike Rust which requires the ? operator for error propagation, Java already automatically propagates checked exceptions when declared in the method signature. What Java needs instead is better support for checked exceptions in functional contexts:

// Current approach for handling exceptions in streams
List<String> contents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new RuntimeException(e); // Lose type information
        }
    })
    .collect(Collectors.toList());

// Hypothetical improved API
List<String> contents = filePaths.stream()
    .mapThrowing(path -> Files.readString(Path.of(path))) // Preserves checked exception
    .onException(IOException.class, e -> logError(e))
    .collect(Collectors.toList());

4. Integration with Optional<T> and Stream<T> APIs

The standard library could be enhanced to better support operations that might throw checked exceptions:

// Hypothetical API
Optional<String> content = Optional.ofThrowable(() -> Files.readString(Path.of("file.txt")));
content.ifPresentOrElse(
    this::processContent,
    exception -> log.error("Failed to read file", exception)
);

Comparison with Other Languages' Approaches

It's worth examining how other languages have addressed the error handling problem:

Rust's Result<T, E> and ? operator

Rust's approach using Result<T, E> and the ? operator shows how propagation can be made concise while keeping the type safety benefits. The ? operator automatically unwraps a successful result or returns the error to the caller, making propagation more elegant.

However, Rust's approach requires explicit propagation at each step, which can be more verbose than Java's automatic propagation in certain scenarios.

Kotlin's Approach

Kotlin made all exceptions unchecked but provides functional constructs like runCatching that bring back some type safety in a more modern way:

val result = runCatching {
    Files.readString(Path.of("file.txt"))
}

result.fold(
    onSuccess = { content -> processContent(content) },
    onFailure = { exception -> log.error("Failed to read file", exception) }
)

This approach works well with Kotlin's functional programming paradigm but lacks compile-time enforcement.

Scala's Try[T], Either[A, B], and Effect Systems

Scala offers Try[T], Either[A, B], and various effect systems that encode errors in the type system while integrating well with functional programming:

import scala.util.Try

val fileContent: Try[String] = Try {
  Source.fromFile("file.txt").mkString
}

fileContent match {
  case Success(content) => processContent(content)
  case Failure(exception) => log.error("Failed to read file", exception)
}

This approach preserves type safety while fitting well with Scala's functional paradigm.

Conclusion

Java's checked exceptions were a pioneering attempt to bring type safety to error handling. While the implementation has shortcomings, the core concept aligns with modern type-safe approaches to error handling in languages like Rust and Haskell.

Copying Rust's Result<T, E> might seem like the obvious solution, but it would represent a radical departure from Java's established paradigms. Instead, targeted enhancements to the existing checked exceptions system—like allowing lambdas to declare exceptions and supporting generic exception types—could preserve Java's unique approach while addressing its practical limitations.

The beauty of such improvements is that they'd maintain backward compatibility while making checked exceptions work seamlessly with modern Java features like lambdas and streams. They would acknowledge that the core concept of checked exceptions was sound—the problem was in the implementation details and their interaction with newer language features.

So rather than abandoning checked exceptions entirely, perhaps we should recognize them as a forward-thinking feature that was implemented before its time. As Java continues to evolve, we have an opportunity to refine this system rather than replace it.

In the meantime, next time you're tempted to disparage checked exceptions, remember: they're not just an annoying Java quirk—they're an early attempt at the same type safety paradigm that newer languages now implement with much celebration.

What do you think? Could these improvements make checked exceptions viable for modern Java development? Or is it too late to salvage this controversial feature? I'm interested in hearing your thoughts in the comments.

Read more →
0
0
3

uv2nix는 uv보다 구리고, cabal2nix는 cabal보다 구린데, Nix는 uv + cabal + ... 보다 낫다. Nix 커뮤니티를 키우려면, 후자를 이해시키고(쉬움) 전자에 대해 익스큐즈하도록 설득해야한다(어려움) .

0
0

@hongminhee洪 民憙 (Hong Minhee) 저는 Nix를 쓰고 있습니다. 한동안 Haskell 안쓰고있다가 오랜만에 돌아왔더니 다들 Nix 쓰고있어서 그냥 따라 쓰는 상태입니다. Nix가 해결하려는 문제와 방향은 공감하지만, Haksell + Nix가 막 엄청 좋은지는 잘 모르겠는 상태에요.

Nix로 그냥 GHC랑 Cabal, Stack 버전만 잡고 나머지는 Cabal, Stack 등의 기존 하스켈 툴링에 맡기는 방법이 있고, 또 Nix가 패키지 다운받아서 빌드하는 역할까지 대신해버리는 방법이 있는데, 제가 쓰고 있는 방법은 후자입니다.

0
0
0

C++ 표준화 위원회(WG21)에게 C++의 원 저자인 비야네 스트롭스트룹Bjarne Stroustrup이 보낸 메일이 이번 달 초에 본인에 의해 공개된 모양이다. C++가 요즘 안전하지 않은 언어라고 열심히 얻어 맞고 있는 게 싫은지 프로파일(P3081)이라고 하는 언어 부분집합을 정의하려고 했는데, 프로파일이 다루는 문제들이 아주 쉬운 것부터 연구가 필요한 것까지 한데 뒤섞여 있어 구현이 매우 까다롭기에 해당 제안이 적절하지 않음을 올해 초에 가멸차게 까는 글(P3586)이 올라 오자 거기에 대한 응답으로 작성된 것으로 보인다. 더 레지스터의 표현을 빌면 "(본지가 아는 한) 스트롭스트룹이 이 정도로 강조해서 말하는 건 2018년 이래 처음"이라나.

여론은 당연히 호의적이지 않은데, 기술적인 반론이 대부분인 P3586과는 달리 해당 메일은 원래 공개 목적이 아니었음을 감안해도 기술적인 얘기는 쏙 빼 놓고 프로파일이 "코드를 안 고치고도 안전성을 가져 갈 수 있다"는 허황된 주장에 기반해 그러니까 프로파일을 당장 집어 넣어야 한다고 주장하고 있으니 그럴 만도 하다. 스트롭스트룹이 그렇게 이름을 언급하지 않으려고 했던 러스트를 굳이 들지 않아도, 애당초 (이 또한 계속 부정하고 싶겠지만) C++의 주요 장점 중 하나였던 강력한 C 호환성이 곧 메모리 안전성의 가장 큰 적이기 때문에 프로파일이 아니라 프로파일 할아버지가 와도 안전성을 진짜로 확보하려면 코드 수정이 필수적이고, 프로파일이 그 문제를 해결한다고 주장하는 건 눈 가리고 아웅이라는 것을 이제는 충분히 많은 사람들이 깨닫지 않았는가. 스트롭스트룹이 허황된 주장을 계속 반복하는 한 C++는 안전해질 기회가 없을 듯 하다.

0
0

시간이 좀 지나서 그런가, 이제 계엄자체는 그냥 또라이가 또라이짓했다 정도로 생각되는데, 그 이후 탄핵 과정에서 뭔 말같지도 않는 어거지를 부리는거 때문에 자꾸 스트레스받는다.

0

그동안 동료들한테 Cursor 쓰자고했는데 그들이 오소독스 Emacs 매니아들이란 문제가 있었다.

작년에 Nix로 nvidia gpu 지원까지 포함해서 구축해놓은 k3s 클러스터에다가, 오늘 아침에 1시간만에 aider로 쓸수있게 DeepSeek R1을 띄웠고 한번 써보자고 했다. 최근에 한 것 중 가장 가성비 좋은 작업인듯 하다.

1
0
0

JS로 짜여진, join이 되는 reactive한 로컬 DB가 필요한데요. RxDB, SignalDB는 join이 안 돼서 탈락입니다. join을 안하면 되지 않냐 할수 있는데 어떤 특이한 로직 때문에 꼭 필요합니다. 지금은 직접 sqlite 호출하는 누더기 코드로 돌아가고 있는데요.

그 코드를 리팩토링해서 제대로 된걸 만들까 말까 고민중인데, 사실 잘만들어진게 있으면 그걸 쓰고 싶습니다. 제 요구사항을 만족하는 라이브러리가 있을까요?

0
0

영어권 웹을 보다보면 autism이 한국어에서의 '자폐'보다 부정적인 늬앙스가 훨씬 덜하단 느낌을 받는다. STEM 너드들이 본인이 autistic하다고 하는 경우를 종종 보는데, 자조적인 느낌이 좀 있지만 완전 딥다크한거 같진않고, 이분법적이기보단 스펙트럼으로 보는거 같다.

0
0

asbubam 님의 내가 만난 멋진 SRE

SRE 에 대한 얘기지만 멋대로(?) 개발자에 대입해서 보았습니다. 와닿는 내용을 일부 인용해 보면

여기 우리 다 처음에 그랬고 오늘도 여전히 매일 고민하고, 부딪히고, 배우면서 앞으로 나아가고 있으니까

멋진 SRE는, 도무지 답이 보이지 않는 문제를 만나도, “원래 그런거니까 흐흐” 하고 문제에 달려들어요. 내 힘으로 부족한 일은, 옆에있는 동료와 함께 반드시 해결할 수 있다고 믿어요

회사 동료들에게도 말해주고 싶은 내용들이네요. 동료들이 있으니께 걱정하지 말고 나아가라구요.

asbubam 님 트위터 링크

0
0
2
0
0

Hackers' Pub에 RSS 기능을 추가했습니다. 정확히는 RFC 4287, 일명 Atom 명세를 구현했습니다. RSS 디스커버리도 구현했기 때문에, Hackers' Pub 사용자의 프로필 페이지 링크를 RSS 앱에 추가하면 구독이 가능합니다. 확실한(?) 피드 링크를 알고 싶으시면 프로필 링크 뒤에 /feed.xml을 붙이시면 됩니다. 예를 들어, 제 피드 링크는 https://hackers.pub/@hongminhee/feed.xml입니다.

Linux용 RSS 앱인 Newsflash에서 Hackers' Pub의 한 사용자의 피드를 구독하여 읽는 모습
0
0

어렸을 때는 Smalltalk나 Lisp 같은 언어에 마음을 많이 빼앗겼는데 (아마도 당시 쿨한 언어였던 Python이나 Ruby의 영향…) Haskell을 접한 뒤로는 언어 취향이 아주 많이 바뀐 것 같다. 일단 동적 타입 언어를… 싫어하는 정도까진 아니지만, 쓰면서 불안함을 느끼게 됐다.

0

Nix를 보며 알수있는건, 사람들이 메타프로그래밍을 하기 좋은 언어로 메타프로그래밍을 하는게 아니라, 런타임이 좋은 언어로 메타프로그래밍을 한다는 것이다.

Nix의 런타임이 좋다는건 일반적인 의미에서(성능이 빠르다거나) 좋다기보다는 '재현가능한 캐싱되는 빌드'라는 런타임이 아주 많은 동작을 커버하는데 Nix가 그걸 구현했다는 얘기다. 그러니까 사람들은 큰 프로그램을 쌓아올릴 대들보가 될만한 런타임이 있으면 거기서 부터 메타프로그래밍을 시작해버린다. Nix가 언어는 구리고(애초에 엄청 잘만들려고 한거같지도 않음) 메타프로그래밍을 잘하기위한 어떠한 장치도 없음에도 가장 아래에 위치할수있어서 그 역할이 맡겨져버린다.

그래서 유용한 런타임과 오브젝트 언어(또는 DSL)을 표현할 문법에 대한 좋은 아이디어가 있으면, 좀더 나은 메타프로그래밍을 하기위한 언어를 만들수 있을거라고 생각한다.

0
0

사실 알고보니 이것도, 저것도 모나드였다... 하는 예시는 많은데 Category의 예시는 뭐가 있을까? 그럼 설명이 훨씬 편해질텐데 말이다.

좀 인위적이지만 쉬운 예시를 하나 만들어보자면, 어떤 함수의 실행에 비용을 부여하는 것이다.

data Costful a b = Costful (a -> IO b) Int

f :: Costful Int String
g :: Costful String Bool

요런 정의를 생각해볼때 f . gfg의 동작은 합성하고, 비용은 +한 것이 될것이다.

instance Category where
 Costful f c1 . Costful g c2 = Costful (f . g) (c1 + c2)

요렇게 말이다. 이때 f . g의 비용은 함수를 실행하기 전에도 알수있다.

반면 그냥 f, g를 모나딕한 함수로 정의하고 f >=> g 이런식으로 합성했을땐, 함수를 실제로 실행하기 전에는 비용을 알수 없다. >=> 또는 >>=의 정의를 생각해보면 쉽게 알수 있다. Category 인스턴스는 정적인 정보를 추가로 가지고 있는 함수, 또는 함수보다 표현력이 약한데 비스무리한거(그래서 정적인 정보가 더많은) 것을 다룰때 유용하다.

0

사실 알고보니 이것도, 저것도 모나드였다... 하는 예시는 많은데 Category의 예시는 뭐가 있을까? 그럼 설명이 훨씬 편해질텐데 말이다.

좀 인위적이지만 쉬운 예시를 하나 만들어보자면, 어떤 함수의 실행에 비용을 부여하는 것이다.

data Costful a b = Costful (a -> IO b) Int

f :: Costful Int String
g :: Costful String Bool

요런 정의를 생각해볼때 f . gfg의 동작은 합성하고, 비용은 +한 것이 될것이다.

instance Category where
 Costful f c1 . Costful g c2 = Costful (f . g) (c1 + c2)

요렇게 말이다. 이때 f . g의 비용은 함수를 실행하기 전에도 알수있다.

반면 그냥 f, g를 모나딕한 함수로 정의하고 f >=> g 이런식으로 합성했을땐, 함수를 실제로 실행하기 전에는 비용을 알수 없다. >=> 또는 >>=의 정의를 생각해보면 쉽게 알수 있다. Category 인스턴스는 정적인 정보를 추가로 가지고 있는 함수, 또는 함수보다 표현력이 약한데 비스무리한거(그래서 정적인 정보가 더많은) 것을 다룰때 유용하다.

0

2022()–2023() 사이에 키우던 강아지랑 어머니, 그리고 아버지께서 次例(차례)로 떠나고 나서 憂鬱症(우울증)으로 到底(도저)히 일을 할 수 없을 것 같아서 退社(퇴사)를 했는데, 家族(가족)葬禮(장례)()憂鬱(우울)退社(퇴사)()한 기쁨이 서로 相殺(상쇄)되어 제로가 된 經驗(경험)이 있다.

0
0

하스켈 패키지 검색 엔진이자 웹 서비스인 후글(Hoogle)은 서비스에 종종 문제가 생기곤 합니다. 그럴 때는 다음과 같은 대체 서비스를 이용해보세요!

한편 후글을 로컬에 설치해서 사용하는 것도 가능합니다. 잦은 서비스 문제에 질렸다면 로컬에 후글을 설치해보세요!

그리고 만약 당신이 부자라면⋯ 하스켈 재단에 기부해주세요⋯

0
0

ActivityPub은 눈팅만 하고있었는데, 슈티에 피드를 넣기로 결정하고나니 ActivityPub를 써볼 기회가 생겼다. 따로 페디버스 서버를 팔지, 아니면 다른 방법을 쓸지(있긴 한가)를 고민해봐야한다.

0
0

쉘스크립트처럼 oci 컨테이너들을 조합하는 언어가 있으면 좋겠다. 컨테이너는 샌드박싱된 파일시스템을 입력으로 받아 출력으로 쓰고, 그런 컨테이너들을 (| pipe operator로 stdin/stdout을 잇듯이) 조합하는 것이다. 그리고 이때 각 컨테이너가 필요로하는 입력 파일/디렉토리들에 대해 일종의 타입 체크를 해서 no such file or directory가 뜨는것을 막아줄수 있을것이다.

사실 yaml등으로 작성하는 CI/CD 설정 파일들이 비슷한 기능을 하고있는데, 이걸 좀더 멀쩡한 언어로, 로컬에서도 쓸수있으면 좋겠다.

0
0

쉘스크립트처럼 oci 컨테이너들을 조합하는 언어가 있으면 좋겠다. 컨테이너는 샌드박싱된 파일시스템을 입력으로 받아 출력으로 쓰고, 그런 컨테이너들을 (| pipe operator로 stdin/stdout을 잇듯이) 조합하는 것이다. 그리고 이때 각 컨테이너가 필요로하는 입력 파일/디렉토리들에 대해 일종의 타입 체크를 해서 no such file or directory가 뜨는것을 막아줄수 있을것이다.

사실 yaml등으로 작성하는 CI/CD 설정 파일들이 비슷한 기능을 하고있는데, 이걸 좀더 멀쩡한 언어로, 로컬에서도 쓸수있으면 좋겠다.

0