What separates the "private WTF language" from just a bad library / API / DSL design?

Some of WTF's most interesting daily stories feature personal languages ​​launched by amok . However, domain-specific languages ​​can be quite powerful and seem to be becoming more and more popular. And, of course, we cannot program at all without good libraries, but as they say, "library design is a language . "

Nobody likes a bad API, but is there only one degree of difference, or is WTF a completely different kind? Obviously, this is subjective, so I made it a wiki community. (The founders of Stackoverflow famously had different opinions about whether one particular internal language even had WTF )

My own guess is that this is an attempt at a community that makes WTF go out, but I would like to see what other people think.

(This question was triggered by reading comments on JaredPar's answer to this question: https://stackoverflow.com/questions/901320/anti-joel-test/901361#901361 )

(To clarify a little more, the term “private language” is often used with negative connotations, while “DSL” or “library” is neutral. Which lines, if any, perform an “internal” tool a way to be ridiculed as an awful “private language”, besides the usual things that can make it a bad tool? It doesn't have to be a language, it can be a library or a framework.)

FINAL EDITOR: I accepted Roger Pate's answer, "Essentially nothing." because I think this is really correct for the question I asked. I would like to emphasize Aaronaught's answer about DSL, although I think this is especially good. Thank.

+3
3

? . ( " /API ", " //API //API.)

, , ( MFC, Qt, GTK,...).

, -, , API, . ( , < stdint.h > .)

, , /API, , " " "DSL". , Qt ( : ) ++, . MFC , .

+3

DSL, , , , , .

  • , DSL Domain- Specific.

DSL , . , DSL ( " " AKA) DSL, . , , .

; tl; dr.


. - API, , :

public abstract class Message
{
    public byte[] GetBytes()
    {
        using (MemoryStream ms = new MemoryStream())
        {
            byte[] result = new byte[ms.Length + 3];
            result[0] = 0xFF;
            result[1] = (byte)ms.Length;
            WriteMessageData(result, 2);
            result[result.Length - 1] = GetChecksum(result, 0,
                result.Length - 2);
            return result;
        }
    }

    protected abstract void WriteMessageData(byte[] buffer, int offset);
}

(). , , , 30 , , , . :

public class AddMessage : Message
{
    private const byte id = 0x9F;

    protected override void WriteMessageData(byte[] buffer, int offset)
    {
        buffer[offset] = id;
        MessageUtil.WriteInt32(buffer, offset + 1, Num1);
        MessageUtil.WriteInt32(buffer, offset + 5, Num2);
    }

    public int Num1 { get; set; }
    public int Num2 { get; set; }
}

, . , . , . . , . , 30 .

. , :

public int Add(int num1, int num2)
{
    AddMessage msg = new AddMessage();
    msg.Num1 = num1;
    msg.Num2 = num2;
    MessagingSystem.SendMessage(msg);
    AddResultMessage result = MessageSystem.Receive<AddResultMessage>();
    if (result == null)
    {
        throw new InvalidResultException("AddResultMessage");
    }
    return result.Sum;
}

--, . . API, . , 10, 20, 50, 100, 1000..., .

, " " -?

Message(Add)
    Send: Num1 int, Num2 int
    Receive: Sum int

Message(Multiply)
    Send: Num1 int, Num2 int
    Receive: Product int

Message(Divide)
    Send: Divisor int, Dividend int
    Receive: Quotient int, Remainder int

, , kludgy-, . , , , -, , , . :

MyMessagingSystem ms = new MyMessagingSystem();
int sum = ms.Add(3, 4).Sum;
int product = ms.Multiply(5, 6).Product;
DivideResult = ms.Divide(10, 5);  // Contains Quotient and Remainder properties

, , DSL ( , ), 20 , OO 3 DSL-.

. . , DSL , , , , , .

( ) "" DSL? . : , , , .

DSL . , , , , /. , . , . , .

, " " DSL; , . , " " . DSL ( , IMO) :

Event: PaymentReceived(Payment)
    Validation:
        Condition: Amount > 0, "Invalid payment amount"
        Condition: Date > Today - 7d, "Cannot backdate > 7 days"
    Actions:
        Update: Account(AccountID)
            SetProperty: Account.Balance, Account.Balance - Payment.Amount
            SetProperty: Account.LastPaymentDate, Payment.Date
        Notify: Billing
            Template: PaymentReceived.xlt
                Field: CustName, CustomerName
                Field: PaymentAmount, Amount
                Field: PaymentDate, Date

.., . . , , !

? ? - , ; , . , , ? ? - , , , Validation . - DSL, :

Event: PaymentReceived(Payment)
    Validation:
        Condition: All(
            PaymentType = Cheque,
            Account(Payment.AccountID).DelinquentFlag = False
        ), "Cheques no longer allowed for this customer"

, , , , "-...". : ", , , ".

, DSL, , :

        Notify: Management
        Condition: All(
            PaymentType = Cheque,
            Account(Payment.AccountID).DelinquentFlag = False
        )
            Template: DelinquentCheque.xlt
                Field: CustName, CustomerName,
                ...

? "" . , . , , DSL .

. ?

, DSL . , , , . , , "" , , , - .

, DSL, -, , , , , . . , . , , DSL, , , , , , .

, " ", , API. . , , DSL, . , . " -", , , Turbo Pascal 1.0.


tl; dr :

, , , "WTF?"? , :

  • . , -, "", "" .

  • , . DSL API, DSL , , , . , , , .

  • . DSL , . - ; / . , , .

  • . , , , , - , , DSL .

, . , !

+6

I think this is due to the principle of least surprise. Well-designed DSLs and APIs do exactly what you expect from them (or do such a large percentage of the time). If you use a good API (and you are smart and experienced), you will find that you say: “It must have a built-in way to do this and that,” and so, the API developer was thinking the same thing. Horrible APIs / private languages ​​have unexpected or inconsistent behavior and make work difficult.

+1
source

Source: https://habr.com/ru/post/1731634/


All Articles